如何在proto3中用上golang对应的interface{}类型
  ZvAhkxhPQwyp 2023年11月02日 67 0
Go

作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!


首先,我希望所有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 proto

import (
	"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.go
package proto

import (
	"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))
}

序列化和反序列化的结果一致。


具体细节请参考这个链接:https://github.com/gogo/protobuf/blob/master/custom_types.md

have fun. 😃

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  uGYzDadp0Cs7   2024年04月18日   58   0   0 Go
  hyrB1Ag4eVs8   2024年04月15日   38   0   0 Go
  YFCZjJLTjJgW   15天前   21   0   0 Go
  uGYzDadp0Cs7   2024年04月16日   93   0   0 Go
ZvAhkxhPQwyp