Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protobuf 3.0 Any Type pack/unpack

Tags:

I would like to know how to transform a Protobuf Any Type to the original Protobuf message type and vice versa. In Java from Message to Any is easy:

Any.Builder anyBuilder = Any.newBuilder().mergeFrom(protoMess.build());

But how can I parse that Any back to the originial message (e.g. to the type of "protoMess")? I could probably parse everything on a stream just to read it back in, but that's not what I want. I want to have some transformation like this:

ProtoMess.MessData.Builder protoMessBuilder = (ProtoMess.MessData.Builder) transformToMessageBuilder(anyBuilder)

How can I achieve that? Is it already implemented for Java? The Protobuf Language Guide says there were pack and unpack methods, but there are none in Java. Thank you in Advance :)

like image 562
Overholt Avatar asked Sep 16 '15 16:09

Overholt


People also ask

How do you use any in protobuf?

To use the Any type, you must import the google/protobuf/any. proto definition. In the C# code, the Any class provides methods for setting the field, extracting the message, and checking the type. Protobuf's internal reflection code uses the Descriptor static field on each generated type to resolve Any field types.

Is proto3 backwards compatible with proto2?

Yes, if some of your systems are proto2 based, it is probably best to keep using proto2. In my opinion, proto3 does not introduce many new features and most libraries will continue supporting proto2. However, the wire format is mostly compatible. As long as the tag number is the same, the encoding remains the same.

Why did proto3 remove optional?

We have seen production issues caused by this multiple times and it's pretty much banned everywhere inside Google for anyone to add/remove required fields. For this reason we completely removed required fields in proto3. After the removal of "required", "optional" is just redundant so we removed "optional" as well.

What is the difference between proto2 and proto3?

Proto3 is the latest version of Protocol Buffers and includes the following changes from proto2: Field presence, also known as hasField , is removed by default for primitive fields. An unset primitive field has a language-defined default value.


2 Answers

The answer might be a bit late but maybe this still helps someone.

In the current version of Protocol Buffers 3 pack and unpack are available in Java.

In your example packing can be done like:

Any anyMessage = Any.pack(protoMess.build()));

And unpacking like:

ProtoMess protoMess = anyMessage.unpack(ProtoMess.class);

Here is also a full example for handling Protocol Buffers messages with nested Any messages:

ProtocolBuffers Files

A simple Protocol Buffers file with a nested Any message could look like:

syntax = "proto3";

import "google/protobuf/any.proto";

message ParentMessage {
  string text = 1;
  google.protobuf.Any childMessage = 2;
}

A possible nested message could then be:

syntax = "proto3";

message ChildMessage {
  string text = 1;
}

Packing

To build the full message the following function can be used:

public ParentMessage createMessage() {
    // Create child message
    ChildMessage.Builder childMessageBuilder = ChildMessage.newBuilder();
    childMessageBuilder.setText("Child Text");
    // Create parent message
    ParentMessage.Builder parentMessageBuilder = ParentMessage.newBuilder();
    parentMessageBuilder.setText("Parent Text");
    parentMessageBuilder.setChildMessage(Any.pack(childMessageBuilder.build()));
    // Return message
    return parentMessageBuilder.build();
}

Unpacking

To read the child message from the parent message the following function can be used:

public ChildMessage readChildMessage(ParentMessage parentMessage) {
    try {
        return parentMessage.getChildMessage().unpack(ChildMessage.class);
    } catch (InvalidProtocolBufferException e) {
        e.printStackTrace();
        return null;
    }
}

EDIT:

If your packed messages can have different Types, you can read out the typeUrl and use reflection to unpack the message. Assuming you have the child messages ChildMessage1 and ChildMessage2 you can do the following:

@SuppressWarnings("unchecked")
public Message readChildMessage(ParentMessage parentMessage) {
    try {
        Any childMessage = parentMessage.getChildMessage();
        String clazzName = childMessage.getTypeUrl().split("/")[1];
        String clazzPackage = String.format("package.%s", clazzName);
        Class<Message> clazz = (Class<Message>) Class.forName(clazzPackage);
        return childMessage.unpack(clazz);
    } catch (ClassNotFoundException | InvalidProtocolBufferException e) {
        e.printStackTrace();
        return null;
    }
}

For further processing, you could determine the type of the message with instanceof, which is not very efficient. If you want to get a message of a certain type, you should compare the typeUrl directly:

public ChildMessage1 readChildMessage(ParentMessage parentMessage) {
    try {
        Any childMessage = parentMessage.getChildMessage();
        String clazzName = childMessage.getTypeUrl().split("/")[1];
        if (clazzName.equals("ChildMessage1")) {
            return childMessage.unpack("ChildMessage1.class");
        }
        return null
    } catch (InvalidProtocolBufferException e) {
        e.printStackTrace();
        return null;
    }
}
like image 133
sundance Avatar answered Sep 20 '22 12:09

sundance


Just to add information in case someone has the same problem ... Currently to unpack you have to do (c# .netcore 3.1 Google.Protobuf 3.11.4)

Foo myobject = anyMessage.Unpack<Foo>();
like image 33
Vonkel. Avatar answered Sep 21 '22 12:09

Vonkel.