darth vader
darth vader

Reputation: 321

is there an example of protobuf with text output?

I want to use protobuf and to create the serialization output file in text format for testing and for a replacement of json. I can't figure out how to write it on my own and am looking for examples. Here is the one on binary output :

#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;

// This function fills in a Person message based on user input.
void PromptForAddress(tutorial::Person* person) {
  cout << "Enter person ID number: ";
  int id;
  cin >> id;
  person->set_id(id);
  cin.ignore(256, '\n');

  cout << "Enter name: ";
  getline(cin, *person->mutable_name());

  cout << "Enter email address (blank for none): ";
  string email;
  getline(cin, email);
  if (!email.empty()) {
    person->set_email(email);
  }

  while (true) {
    cout << "Enter a phone number (or leave blank to finish): ";
    string number;
    getline(cin, number);
    if (number.empty()) {
      break;
    }

    tutorial::Person::PhoneNumber* phone_number = person->add_phones();
    phone_number->set_number(number);

    cout << "Is this a mobile, home, or work phone? ";
    string type;
    getline(cin, type);
    if (type == "mobile") {
      phone_number->set_type(tutorial::Person::MOBILE);
    } else if (type == "home") {
      phone_number->set_type(tutorial::Person::HOME);
    } else if (type == "work") {
      phone_number->set_type(tutorial::Person::WORK);
    } else {
      cout << "Unknown phone type.  Using default." << endl;
    }
  }
}

// Main function:  Reads the entire address book from a file,
//   adds one person based on user input, then writes it back out to the same
//   file.
int main(int argc, char* argv[]) {
  // Verify that the version of the library that we linked against is
  // compatible with the version of the headers we compiled against.
  GOOGLE_PROTOBUF_VERIFY_VERSION;

  if (argc != 2) {
    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
    return -1;
  }

  tutorial::AddressBook address_book;

  {
    // Read the existing address book.
    fstream input(argv[1], ios::in | ios::binary);
    if (!input) {
      cout << argv[1] << ": File not found.  Creating a new file." << endl;
    } else if (!address_book.ParseFromIstream(&input)) {
      cerr << "Failed to parse address book." << endl;
      return -1;
    }
  }

  // Add an address.
  PromptForAddress(address_book.add_people());

  {
    // Write the new address book back to disk.
    fstream output(argv[1], ios::out | ios::trunc | ios::binary);
    if (!address_book.SerializeToOstream(&output)) {
      cerr << "Failed to write address book." << endl;
      return -1;
    }
  }

  // Optional:  Delete all global objects allocated by libprotobuf.
  google::protobuf::ShutdownProtobufLibrary();

  return 0;
}

Can I just do some minor changes in this one to output in text format or something else needs to be done? Please either suggest the changes required or any link where code exists (in any language).

Upvotes: 2

Views: 35119

Answers (3)

thegreendroid
thegreendroid

Reputation: 3328

To convert a message to JSON in three lines of code, do this -

#include <google/protobuf/util/json_util.h>

static std::string ProtoToJson(const google::protobuf::Message& proto)
{
  std::string json;
  google::protobuf::util::MessageToJsonString(proto, &json);
  return json;
}

Upvotes: 2

Richard Hodges
Richard Hodges

Reputation: 69912

This code will serialise protobuf messages to JSON and deserialise JSON to protobuf messages.

This is lifted straight out of production code (which I own and hereby grant you licence to use, but please credit me).

This is linked against protobuf 3.

Header:

struct pretty_json_type {
    void operator()(google::protobuf::util::JsonOptions& opts) const {
        opts.add_whitespace = true;
    }
};
static constexpr pretty_json_type pretty_json{};

struct compact_json_type {
    void operator()(google::protobuf::util::JsonOptions& opts) const {
        opts.add_whitespace = false;
    }
};
static constexpr compact_json_type compact_json{};

struct include_defaults_type {
    void operator()(google::protobuf::util::JsonOptions& opts) const {
        opts.always_print_primitive_fields = true;
    }
};
static constexpr include_defaults_type include_defaults{};

template<class...Options>
auto json_options(Options&&...options)
{
    google::protobuf::util::JsonOptions opts;
    using expand = int [];
    void(expand{
        0,
        ((options(opts)),0)...
    });
    return opts;
}

std::string as_json(const google::protobuf::Message& msg,
                    google::protobuf::util::JsonOptions opts = json_options(pretty_json,
                                                                            include_defaults));

std::string as_json(const google::protobuf::Message* msg,
                    google::protobuf::util::JsonOptions opts = json_options(pretty_json,
                                                                            include_defaults));

google::protobuf::Message& from_json(google::protobuf::Message& msg,
                                     const char* first,
                                     std::size_t size);

inline
decltype(auto) from_json(google::protobuf::Message& msg,
                         const std::string& json)
{
    return from_json(msg, json.data(), json.length());
}

Implementation

std::string as_json(const google::protobuf::Message& msg,
                    google::protobuf::util::JsonOptions opts)
{
    namespace pb = google::protobuf;
    namespace pbu = google::protobuf::util;

    auto buffer = msg.SerializeAsString();
    std::string result;
    pb::io::ArrayInputStream zistream(buffer.data(), buffer.size());

    auto resolver = std::unique_ptr<pbu::TypeResolver> {
        pbu::NewTypeResolverForDescriptorPool("",
                                              pb::DescriptorPool::generated_pool())
    };

    auto status = google::protobuf::util::BinaryToJsonString(resolver.get(),
                                                             "/" + msg.GetDescriptor()->full_name(),
                                                             buffer,
                                                             std::addressof(result),
                                                             opts);
    if (!status.ok())
    {
        std::ostringstream ss;
        ss << status;
        throw std::runtime_error(ss.str());
    }
    return result;
}

std::string as_json(const google::protobuf::Message* msg,
                    google::protobuf::util::JsonOptions opts)
{
    return as_json(*msg, opts);
}


google::protobuf::Message& from_json(google::protobuf::Message& msg,
                                     const char* first,
                                     std::size_t size)
{
    namespace pb = google::protobuf;
    namespace pbu = google::protobuf::util;

    auto resolver = std::unique_ptr<pbu::TypeResolver> {
        pbu::NewTypeResolverForDescriptorPool("", pb::DescriptorPool::generated_pool())
    };

    auto zistream = std::make_unique<pb::io::ArrayInputStream>(first,
                                                               size);
    auto binary_buffer = std::string {};
    binary_buffer.reserve(size);
    auto zostream = std::make_unique<pb::io::StringOutputStream>(std::addressof(binary_buffer));

    auto status = pbu::JsonToBinaryStream(resolver.get(),
                                          "/" + msg.GetDescriptor()->full_name(),
                                          zistream.get(), zostream.get());
    zistream.reset();
    zostream.reset();
    if (msg.ParseFromString(binary_buffer))
    {
        return msg;
    }

    throw std::runtime_error("invalid message");
}

Upvotes: 3

Kerrek SB
Kerrek SB

Reputation: 477504

The debug string output is guaranteed to be valid text-serialized format, but does not care about whether the protocol message is actually valid:

std::string s = msg.DebugString();  // or ShortDebugString

If you want to validate, use TextFormat::PrintToString:

#include <google/protobuf/text_format.h>

if (std::string s; google::protobuf::TextFormat::PrintToString(msg, &s)) {
  std::cout << "Your message: " << s;
} else {
  std::cerr << "Message not valid (partial content: "
            << msg.ShortDebugString() << ")\n";
}

Tools for JSON interop are available in json_util.h.

Upvotes: 18

Related Questions