How to take an arbitrary proto message as input and call protojson.Marshal with it

606 views Asked by At

I'm writing a function that takes an arbitrary proto message and does something with it

func protoToSomething(in proto.Message) ([]byte, error) {
    jsonBytes, protoJsonError := protojson.Marshal(in)
    if protoJsonError != nil {
        return nil, protoJsonError
    }
   stuff here...
}

However when I call it with this:

    model := myprotolib.MyRequest{
        UserId:        "3214",
        UserName:      "JohnDoe",
        SomeValue:     true,
    }
    data, _ := protoToSomething(model)

I get this "myprotolib.MyRequest does not implement protoreflect.ProtoMessage (ProtoReflect method has pointer receiver)"

This looks accurate because when I look at the autogenerated proto code, it is defined with

func (x *MyRequest) ProtoReflect() protoreflect.Message {

(Where the receiver takes a pointer.)

But when I look at the definition in protoreflect/proto.go I see

type ProtoMessage interface{ ProtoReflect() Message }

So I'm confused, and new to Go (though I've used Protos, Java, and C++ professionally) -- how do I write a function that takes an arbitrary proto message and does some stuff to it, including protojson.Marshall?

1

There are 1 answers

2
Burak Serdar On

model does not implement that interface, because model declared that method for a pointer receiver.

If a method is declared with a pointer receiver, only pointers of that type implement that interface, not the value types. That is:

type I interface {
   f()
}

type S struct {
}

func (s S) f() {}

type T struct {
}

func (t *T) f() {}

func W(intf I) {}

func main() {
   s:=S{}
   W(s)  // This works. S has method f()
   W(&s) // This works. *S has method f()
   t:=T{}
   W(t) // This does not work. T does not have f()
   w(&t) // This works. *T has f()
}

This essentially prevents inadvertently passing a copy of a value where a pointer is needed.