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

Casting Any to proto.Message #476

Open
AlmogBaku opened this issue Sep 5, 2018 · 8 comments
Open

Casting Any to proto.Message #476

AlmogBaku opened this issue Sep 5, 2018 · 8 comments
Labels

Comments

@AlmogBaku
Copy link
Contributor

AlmogBaku commented Sep 5, 2018

Is there a way to cast the WKT Any to a proto.Message like we doing with the stdtime in order to reduce boilerplate?

@jmarais
Copy link
Contributor

jmarais commented Sep 5, 2018

Hey.
Could you clarify your question please?
Do have an example of the boilerplate you are experiencing?

@AlmogBaku
Copy link
Contributor Author

AlmogBaku commented Sep 6, 2018

given the message:

message EventMessage {
    string event = 1;
    google.protobuf.Any value = 2;
}

This will be compiled to:

type EventMessage struct {
	Event string               `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"`
	Value *google_protobuf.Any `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
}

It will be nice if the Value will be compiled to the message itself, and will be "proto.Message"(inteface) in the struct definition

@jmarais
Copy link
Contributor

jmarais commented Sep 6, 2018

So the Value field should already implement the "proto.Message" interface.
The interface: https://github.com/gogo/protobuf/blob/master/proto/lib.go#L280:L284

And Any implements the proto.Message interface with these methods in the file:
Reset()
String() string
ProtoMessage()

So you will be able to use the value where you need a proto.Message type.

func foo() {
	em := &EventMessage{}
	myProtoMessageFunc(em.Value)
}

func myProtoMessageFunc(pb proto.Message) {
	fmt.Printf("Here is a pb message: %+v\n", pb)
}

Is that what you meant?

With regards to un/marshaling a any type. Here are some helper functions:
MarshalAny
UnmarshalAny
DynamicAny

Edit:

It will be nice if the Value will be compiled to the message itself, and will be "proto.Message"(inteface) in the struct definition

Did you mean if you unmarshal a EventMessage you want the Value field to automatically unmarshal the Any type as a proto.Message interface well?. So your struct would look like this:

type EventMessage struct {
	Event                string               `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"`
	Value                 proto.Message `protobuf:"bytes,2,opt,name=value" json:"myany,omitempty"`
}

And then the ```Value``` field would be the Unmarshaled message which was inside the ```Any``` ?

@AlmogBaku
Copy link
Contributor Author

AlmogBaku commented Sep 11, 2018

Exactly- when you write:

message EventMessage {
    string event = 1;
    google.protobuf.Any value = 2 [gogo.anyprotofmt=true];
}

It'll be compiled to:

type EventMessage struct {
	Event string               `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"`
	Value *proto.Message `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
}

And you'll be able to use it as:

msg := EventMessage{}
msg.Value = &SomeOtherMessage{}

And behind the scene- it'll use Any.

Since the Any/Message is implemented on the google/gogo side, I can't use it as a custom field

@AlmogBaku
Copy link
Contributor Author

AlmogBaku commented Sep 11, 2018

To be exact- the Idea is that when you do Unmarshal for the EvenMessage it'll also unmarshal it's children(Value)- and you'll just need to cast it to the right type.

@jmarais
Copy link
Contributor

jmarais commented Sep 12, 2018

Ah ok, Thanks for the clarification.
Do you have an example of the boilerplate you are experiencing? Can you provide a short code example?

@AlmogBaku
Copy link
Contributor Author

It's exactly like the stdtime issue- we need to add a few lines to convert the google timestamp to the standard time... I think it can be useful to do that seamlessly by gogo.

If you can hint a little maybe ill be able to draft something

@jmarais
Copy link
Contributor

jmarais commented Sep 15, 2018

Hey, sorry for the wait, I had to think about it a bit. I am still unsure about the boilerplate you are experiencing.

Here is a simple use case:
protobuf file:

message EventMessage {
	string name = 1;
	google.protobuf.Any myany = 2;
}

message Foo {
	string bar = 1;
}

simple go example:

	event := &EventMessage{Name: "firstevent"}

	myAnyMessage := &Foo{
		Bar: "bar",
	}
	marshaledAny, err := types.MarshalAny(myAnyMessage)
	if err != nil {
		panic(err)
	}

	event.Myany = marshaledAny

	b, err := proto.Marshal(event)
	if err != nil {
		panic(err)
	}

	receivedEvent := &EventMessage{}
	proto.Unmarshal(b, receivedEvent)

	newReceivedAny := &Foo{}
	err = types.UnmarshalAny(receivedEvent.Myany, newReceivedAny)
	if err != nil {
		panic(err)
	}
	fmt.Printf("AnyMessage: %#v\n", newReceivedAny)
	pbMessage(newReceivedAny)

Currently you have to unmarshal the any type in your struct when you receive it

	newReceivedAny := &Foo{}
	err = types.UnmarshalAny(receivedEvent.Myany, newReceivedAny)
	if err != nil {
		panic(err)
	}

However, even if we "auto" unmarshal it to a proto.Message, you would still need to use a type switch to get the correct type like this:

	switch t := protoMessage.(type) {
	case *Foo:
		fmt.Printf("Foo message: %v\n", pb)
	default:
		fmt.Printf("Unknown message of type: %v, %T\n", t, pb)
	}

So you would just get different "boilerplate" you have to write?
What did you have in mind?

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

No branches or pull requests

3 participants