[БЕЗ_ЗВУКА] В предыдущем видео мы с вами разобрали, какие методы генерируются для полей разных типов в protobuf-сообщениях. В этом видео мы разберем методы, которые являются общими для всех protobuf-сообщений и используются для их сериализации и десериализации. Все классы, представляющие сообщения в protobuf, являются наследниками единого базового класса google::protobuf::Message. И этот базовый класс обладает целым набором методов для сериализации сообщений. Вы их можете видеть на экране, и давайте мы с вами разберем применение одного из них, а именно метода SerializeToOstream. Переключимся в Eclipse, и у нас здесь уже с предыдущего видео осталось сформированное protobuf-сообщение класса Person, и давайте мы сериализуем его в выходной файл. Для этого мы создадим выходной поток и будем писать в файл, например, назовем его person.bin, и не забудем открыть его в бинарном режиме, binary. И теперь простым методом SerializeToOstream мы сохраним наше сообщение в этот файл. Давайте скомпилируем нашу программу и, когда она закончит компилироваться, запустим. Она отработала, у нас появился какой-то вывод, который у нас опять же остался с предыдущего видео, но сейчас мы давайте сделаем другое. Мы переключимся в консоль, вот мы видим, что у нас здесь есть файл person.bin, который мы только что создали, и с помощью утилиты Hex Dump давайте мы посмотрим его содержимое. Мы видим здесь какой-то набор байтов, при этом видим такие строки, как Льва Толстого, +7-123-456-77-88, то есть это те адреса и номера телефонов, которые мы задали для нашего контакта в коде на C++. То есть мы видим, что наше сообщение успешно сериализовалось. Отлично. Идем дальше. У этого же класса google::protobuf::Message есть набор методов для десериализации сообщений, и мы опять же сейчас посмотрим, как работает метод, загружающий сообщения из потока ввода. При этом вы можете видеть, что все эти методы возвращают bool, то есть они могут сообщить нам, если, о том, что десериализация не удалась. И давайте мы напишем код, который из файла person.bin загрузит сохраненную там, сохраненную там информацию. Значит, создаем поток для чтения из файла, только предварительно мы закроем файл person.bin, чтобы туда точно все сохранилось. Здесь тоже указываем person.bin, тоже не забываем указать бинарный режим, объявляем новую переменную, в которую мы будем выполнять десериализацию, и дальше делаем так. Если parsed_person. .ParseFromIstream(&inp), если у нас удалось, нам удалось успешно загрузить данные из входного потока, то давайте выведем, например, имя нашей, нашего контакта, parsed, parsed_person.name, перевод строки, дальше выведем, например, количество номеров телефонов, которые мы загрузили, parsed_person. .phone_number_size. Тут пробел не забудем и проверим, что если у нашего контакта есть адрес, то выведем, например, улицу, [ЗВУК] обратившись к методу address, в него выведем улицу. Если же адреса нет, то давайте выведем, что адреса нету. Ну и, наконец, к if, который у нас выполняет загрузку, мы добавим часть else, которая сообщит, что нам не удалось разобрать сообщение. При этом я закомментирую вывод, который у нас идет выше, потому что он нас сейчас уже не интересует, и давайте скомпилируем нашу программу, она у нас компилируется, и запустим. Она отработала, и мы можем видеть, что у нас вывелась строчка name, после которой ничего нет, потом мы видим, что у нашего контакта два номера телефона, и это как раз столько, сколько мы добавили с помощью вызова методов add_phone_number, и находится наш контакт на улице Льва Толстого. Таким образом, мы видим, что наш контакт загрузился успешно. При этом давайте обратим внимание на то, что, когда мы формируем сообщение Serialize::Person в коде на C++, мы нашему контакту не задаем имя, у нас нигде в коде нет вызова метода set_name. При этом наш контакт успешно сериализовался и успешно десериализовался, и мы увидели, что у него имя пустое. И здесь нам надо обсудить такой довольно важный вопрос — это значения по умолчанию у полей protobuf-сообщений, потому что, как мы только что увидели, любое поле в protobuf-сообщении может отсутствовать, у него может не быть значения. И надо понимать, чему же будет тогда равно его значение, если мы просто вызовем соответствующий метод, как в данном случае мы вызываем метод name у объекта parsed_person. Здесь есть разница между типами полей. Если мы говорим, если мы говорим о полях целых типов или о полях типа string, то по умолчанию они равны, соответственно, нулю или пустой строке. То есть если мы не задали значение этого поля, но к нему обращаемся, то мы получим либо ноль, либо пустую строку. Это гарантируется библиотекой protobuf. Если же наше поле имеет тип какого-то другого сообщения, то в сообщении будет специальный метод, название которого начинается на has_, и вы можете видеть, что мы им воспользовались, вызвав метод has_address. Мы можем проверить, есть ли в нашем сообщении поле address, которое является, имеет тип в нашем случае Serialize::Address. То есть разница в том, что для полей простых типов у нас нет возможности проверить, установлено оно или нет, у нас просто есть значение по умолчанию, для полей, которые сами по себе являются сообщениями, у нас есть возможность проверить, установлено это поле или нет. Итак, давайте подведем итоги. В этом видео мы с вами познакомились с методами сериализации и десериализации protobuf-сообщений, мы узнали отличия в API между встроенными типами и типами сообщений, и теперь мы можем подвести итог и сказать, что вы готовы использовать protobuf в проектах на C++, и после этого видео вам предстоит это сделать.