需求场景
日常开发中,使用kratos框架,基于此希望使用proto3来定义全部的http请求响应的结构。
- 某个字段的类型可能是动态的,对应的JSON类型可能是number/string/boolean/null中的其中一种。
- 某个请求响应结构体是很复杂的,希望直接响应json类型返回到前端,后端无需处理该响应结构体的内部信息。
解决方案
- kratos 框架默认引用的官方protobuf
https://github.com/protocolbuffers/protobuf - 引入第三方protobuf
https://github.com/gogo/protobuf
实现
1. 默认protobuf
proto文件编写
# my.proto
message MyReply{
bytes value = 1;
}
序列化
# my.go
import "encoding/json"
func NewMyReply(v interface{}) *MyReply {
s, err := json.Marshal(v)
if err != nil {
return &MyReply{
Value: []byte(""),
}
}
return &MyReply{
Value: s,
}
}
func (x *MyReply) MarshalJSON() ([]byte, error) {
return x.Value, nil
}
func (x *MyReply) UnmarshalJSON(v []byte) error {
x.Value = v
return nil
}
测试
# my_test.go
func Test_MyReply(t *testing.T) {
j := `{
"explorer": {
"minBuildNumber": 0,
"assetBundlesFetchUrl":"xxxxx"
},
"servers": {
"contentWhitelist": [
"xxxxx"
]
}
}
`
inst := &MyReply{}
err := json.Unmarshal([]byte(j), inst)
if err != nil {
t.Errorf("json decode error, err=%+v", err)
return
}
str, err := json.Marshal(inst)
if err != nil {
t.Errorf("json encode error, err=%+v", err)
return
}
t.Logf("json=%s", string(str))
}
2. 第三方gogoprotobuf
proto文件编写
# mygogo.proto
message MyReply{
bytes value = 1[(gogoproto.customtype) = "InterfaceType"]; // InterfaceType为自定义类型
}
序列化
# mygogo.go
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
}
func (t InterfaceType) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *InterfaceType) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
测试同上
...