Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to write proto data without code generation?

I would like to know if it's possible to use the reflection API provided by google protobuf to serialize messages without code generation?

Protocol buffer allows us to use reflection on Message or Message.Builder objects after parsing process. But in my case, I would like to know if I can fill these object with fields/values, then write them into a file.

like image 236
Gaetan E. Avatar asked Jun 25 '13 07:06

Gaetan E.


People also ask

How does Protobuf encode data?

A protocol buffer message is a series of key-value pairs. The binary version of a message just uses the field's number as the key -- the name and declared type for each field can only be determined on the decoding end by referencing the message type's definition (that is, the . proto file).

What language are proto files written in?

The protoc compiler is written in C or C++ (its a native program anyway). its compilcated because options because the protoc command and options can be stored in a properties file.

How do I create a proto file?

proto file are simple: you add a message for each data structure you want to serialize, then specify a name and a type for each field in the message. Here is the . proto file that defines your messages, addressbook. proto .

Is Protobuf faster than JSON?

TL;DR — encoding and decoding string-intensive data in JavaScript is faster with JSON than it is with protobuf. When you have structured data in JavaScript, which needs to be sent over the network (for another microservice for example) or saved into a storage system, it first needs to be serialized.


2 Answers

CodedOutputStream

One way of doing this is by understanding how a message is encoded and use the CodedOutputStream to write message fields with the appropriate write*() methods.

E.g. to write the following message:

message MyMessage {
    int foo = 1;
    string bar = 2;
}

You would use this piece of code:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
CodedOutputStream out = CodedOutputStream.newInstance(baos);
out.writeInt32(1, 1);
out.writeString(2, "s");
out.flush();
byte[] rawProtocolBuffer = baos.toByteArray();

DynamicMessage

Another way is to create the descriptors by hand and then use DynamicMessage to set the respective fields, but this is more boilerplate than using CodedOutputStream directly.

String messageName = "MyMessage";
FileDescriptorProto fileDescriptorProto = FileDescriptorProto
        .newBuilder()
        .addMessageType(DescriptorProto.newBuilder()
                .setName(messageName)
                .addField(FieldDescriptorProto.newBuilder()
                        .setName("foo")
                        .setNumber(1)
                        .setType(FieldDescriptorProto.Type.TYPE_INT32)
                        .build())
                .addField(FieldDescriptorProto.newBuilder()
                        .setName("bar")
                        .setNumber(2)
                        .setType(FieldDescriptorProto.Type.TYPE_STRING)
                        .build())
                .build())
        .build();

Descriptor messageDescriptor = FileDescriptor
        .buildFrom(fileDescriptorProto, new FileDescriptor[0])
        .findMessageTypeByName(messageName);

DynamicMessage message = DynamicMessage
        .newBuilder(messageDescriptor)
        .setField(messageDescriptor.findFieldByNumber(1), 1)
        .setField(messageDescriptor.findFieldByName("bar"), "s")
        .build();

byte[] rawProtocolBuffer = message.toByteArray();
like image 93
Maik Avatar answered Dec 01 '22 16:12

Maik


First you need to compile the proto file into a desc file.

protoc --descriptor_set_out=point.desc --include_imports point.proto

Then, use the desc file to encode and decode messages.

InputStream input = new FileInputStream("point.desc");
DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(input);
DescriptorProtos.FileDescriptorProto fileDescriptorProto = descriptorSet.getFile(0);

Descriptors.Descriptor messageDescriptor = Descriptors.FileDescriptor
        .buildFrom(fileDescriptorProto, new Descriptors.FileDescriptor[0])
        .findMessageTypeByName("Point");


// Encoding
DynamicMessage message = DynamicMessage
        .newBuilder(messageDescriptor)
        .setField(messageDescriptor.findFieldByNumber(1), 10)
        .setField(messageDescriptor.findFieldByNumber(2), 5)
        .setField(messageDescriptor.findFieldByName("label"), "test label")
        .build();

byte[] encodedBytes = message.toByteArray();


// Decoding
DynamicMessage dynamicMessage = DynamicMessage.parseFrom(messageDescriptor, encodedBytes);

int x = (int) dynamicMessage.getField(messageDescriptor.findFieldByName("x"));
int y = (int) dynamicMessage.getField(messageDescriptor.findFieldByName("y"));
String label = (String) dynamicMessage.getField(messageDescriptor.findFieldByName("label"));
like image 32
Lahiru Chandima Avatar answered Dec 01 '22 16:12

Lahiru Chandima