首先,我希望所有golang中用于http请求响应的结构,都使用proto3来定义。
麻烦的是,有的情况下某个字段的类型可能是动态的,对应的JSON类型可能是number/string/boolean/null中的其中一种。
一开始我尝试用proto.Any类型,就像这样:
import "google/protobuf/any.proto";
message MyRequest{
google.protobuf.Any user_input = 1; // 用户可能输入 number / string / boolean / null 中的其中一种}
使用protoc生成代码后,发现这玩意儿完全没办法做json的encode/decode。
理想的办法是让生成golang代码中的 user_input 成为 interface{} 类型。但如何才能让proto3生成golang的interface类型呢?
尝试后发现可以用下面的办法解决:
1.使用gogo proto的扩展语法import "google/protobuf/descriptor.proto";import "github.com/gogo/protobuf/gogoproto/gogo.proto";message MyRequest{ bytes user_input = 1[(gogoproto.customtype) = "InterfaceType"]; // 使用一个叫做 InterfaceType 的自定义类型}
注意:InterfaceType 直接写成 interface{} 是不行的。因为 interface{} 类型没有实现序列化的接口。
执行protoc后生成了如下代码:
type MyRequest struct {
UserInput []InterfaceType `protobuf:"bytes,4,rep,name=user_input,proto3,customtype=InterfaceType" json:"user_input,omitempty"`}
2. 编写 InterfaceType 类型对应的序列化代码// interface_type.go// 放在xxx.pb.go的同一目录下package protoimport ( "encoding/json"
"errors")type InterfaceType struct {
Value interface{}
}func (t InterfaceType) Marshal() ([]byte, error) { return nil, errors.New("not implement")
}func (t *InterfaceType) MarshalTo(data []byte) (n int, err error) { return 0, errors.New("not implement")
}func (t *InterfaceType) Unmarshal(data []byte) error { return errors.New("not implement")
}func (t *InterfaceType) Size() int { return -1}// 因为只做JSON的序列化,所以只实现这两个方法就行了func (t InterfaceType) MarshalJSON() ([]byte, error) { return json.Marshal(t.Value)
}func (t *InterfaceType) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &t.Value)
}
3.测试一下// my_request.pb_test.gopackage protoimport ( "encoding/json"
"testing")func Test_MyRequest(t *testing.T) {
j := `{"user_input":123}`
inst := &MyRequest{}
err := json.Unmarshal([]byte(j), inst) if err != nil {
t.Errorf("json decode error, err=%+v", err) return
}
t.Logf("%+v", MyRequest)
str, err := json.Marshal(inst) if err != nil {
t.Errorf("json encode error, err=%+v", err) return
}
t.Logf("json=%s", string(str))
}
序列化和反序列化的结果一致。