Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example for how to work with oneofs #523

Closed
spiderkeys opened this issue Dec 7, 2016 · 6 comments
Closed

Example for how to work with oneofs #523

spiderkeys opened this issue Dec 7, 2016 · 6 comments

Comments

@spiderkeys
Copy link

I've been hunting around for some up to date documentation that explains the basics of how to work with oneof fields in protobuf.js v6, but haven't been able to find anything. If there are some examples, could you point me in the right direction, and if not, would you mind giving a brief example of creating, encoding, and decoding a message that has a oneof field? Thanks.

@spiderkeys
Copy link
Author

After messing around a bit longer, I got to something that seems to be working, but I'm not sure if this is the proper approach:

For a proto file "simple.proto":

syntax = "proto2";

message MsgType1 {
    required int32 value = 1;
}

message MsgType2 {
    required bool value = 1;
}

message MsgType3 {
    required int32 value1 = 1;
    required int32 value2 = 2;
}

message MyMessage {
    required uint32 uid = 1;
    required uint32 pid = 2;
    required uint32 utime = 3;

    oneof payload {
        MsgType1 msg1 = 4;
        MsgType2 msg2 = 5;
        MsgType3 msg3 = 6;
    }
}
const protobuf = require( "protobufjs" );

protobuf.load("simple.proto", function(err, root) 
{
    if (err) throw err;
    
    // Lookup message type
    var MyMessage = root.lookup("MyMessage");

    // Create
    var myMessage = MyMessage.create( 
    {
        uid:1,
        pid:2,
        utime:3,
        msg2: { value: true},
        msg1: { value: 7}
    });

    // Encode
    var bb = MyMessage.encode( myMessage ).finish();

    // Decode
    var decodedMessage = MyMessage.decode(bb);
    var which = decodedMessage.payload;
})

Anything more to this? More idiomatic approach?

@dcodeIO
Copy link
Member

dcodeIO commented Dec 7, 2016

Using MyMessage.create (just as you do) instead of plain objects is recommended when using oneofs, because runtime messages have virtual oneof properties with proper side effects.

    var myMessage = MyMessage.create( 
    {
        uid:1,
        pid:2,
        utime:3,
        msg2: { value: true},
        msg1: { value: 7}
    });

When creating a message with oneofs, the last declared field value that is part of the oneof will cause the virtual oneof field to be set to this field's name (here: payload = "msg1"). Other field values part of the same oneof will be deleted (here: msg2, only msg1 is encoded). (see: Prototype constructor, setVirtual)

Setting a oneof field will automatically clear all other members of the oneof. So if you set several oneof fields, only the last field you set will still have a value. 1

When decoding, the virtual oneof field should point to the field name of the last field of the oneof that was present on the wire. However, if multiple fields were present (which should be prevented usually), all of the present field values are set on the wire, but the virtual field will still just point to the last seen on the wire. (see getVirtual - this actually returns the first defined oneof field's name present on the wire, which is not to the spec and should be fixed).

If the parser encounters multiple members of the same oneof on the wire, only the last member seen is used in the parsed message. 1

When working with oneofs, like changing the field, you can set the virtual oneof field to the field name of the field you want to be the sole field to encode (see setVirtual above):

myMessage.msg2 = ... ;
myMessage.payload = "msg2"; // deletes msg1 - use .setPayload("msg2") to support IE8

@spiderkeys
Copy link
Author

Great, thanks for the additional information and for the great work you've put into this!

@opengpu
Copy link

opengpu commented Jul 8, 2018

is there other better way for decodedMsg than the code below?
switch(decodedMessage.payload)
{
case "msg1":
break;
case "msg2":
break;
case "msg3":
break;
case "msg4":
break;
defalut:
break;
}
/////////////////////////////////////////////////////
as i know in c++, protobuf have a _case() function and a related enum, so it can be like this:
switch(pDecodedMsg->payload_case())
{
case package::MyMessage::kMsgType1:
break;
case package::MyMessage::kMsgType2:
break;
case package::MyMessage::kMsgType3:
break;
...
}

@doom-goober
Copy link

When working with oneofs, like changing the field, you can set the virtual oneof field to the field name of the field you want to be the sole field to encode (see setVirtual above):

myMessage.msg2 = ... ;
myMessage.payload = "msg2"; // deletes msg1 - use .setPayload("msg2") to support IE8

I feel like this should be in the documentation as it's a pretty common case.

@xL0b0
Copy link

xL0b0 commented Oct 26, 2021

Can someone possibly help me with the JSON integration of this?

The object I'd like to encode looks like this:

 let payload = {
         age: 12,
         bio: {
             hair: "blonde", gender: "male"
         }
     }

My JSON:

        let json = {
            "nested":
            {
                "package":
                {
                    "nested":
                    {
                        "payload":
                        {
                            "fields":
                            {
                                "age":
                                {
                                    "type": "uint64",
                                    "id": 1
                                },
                                "bio": [
                                    {
                                        "type": "string",
                                        "id": 1
                                    },
                                ]
                            }
                        }
                    }
                }
            }
        }

I know that the JSON is faulty with regard to the bio field, as I am just unsure on how to properly format this.

Any help is much appreciated, thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants