Go语言使用protojson库实现Protocol Buffers与JSON转换
作者:jefffff
本文主要介绍Google开源的工具库Protojson库如何Protocol Buffers与JSON进行转换,以及和标准库encoding/json的性能对比,需要的朋友可以参考下
Protojson 简介
Protojson是Google针对Protocol Buffers数据格式的JSON编码库,为Go语言开发人员提供了便捷的工具和API,用于Protocol Buffers消息与JSON之间的转换。常用API:
func Format(m proto.Message) string func Marshal(m proto.Message) ([]byte, error) func Unmarshal(b []byte, m proto.Message) error type MarshalOptions func (o MarshalOptions) Format(m proto.Message) string func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) func (o MarshalOptions) MarshalAppend(b []byte, m proto.Message) ([]byte, error) type UnmarshalOptions func (o UnmarshalOptions) Unmarshal(b []byte, m proto.Message) error
接着,我们通过一些实践例子来演示如何使用。
安装protoc
- 设置Go Env(window os)
# go env -w GOPROXY=https://goproxy.cn # go env -w GOBIN=%USERPROFILE%\go\bin
其中 USERPROFILE 为默认用户安装路径,example: C:\Users\jeffff
- 下载安装
下载适合自己os的protoc版本,复制到GOBIN目录下,下载链接https://github.com/protocolbuffers/protobuf/releases
- 检查是否安装成功
# protoc --version libprotoc 24.3
安装protoc-gen-go
这里采用 go install安装,安装成功后会添加到GOBIN目录下
# go install google.golang.org/protobuf/cmd/protoc-gen-go@latest # protoc-gen-go --version protoc-gen-go v1.31.0
Example
创建/example/example.proto 创建/exmpale/main.go
syntax = "proto3"; package example.pb; option go_package = "./;pb"; message User { string id = 1; string name = 3; int32 age = 4; string real_name = 5; string date = 8; }
执行如下命令,生成/example/example.pb.go文件
#protoc --go_out=. example.proto
创建main.go,快速实践各个API的功能
package main import ( "fmt" "github.com/google/uuid" "github.com/luckytking/practices/libs/protojson/pb" "google.golang.org/protobuf/encoding/protojson" ) func userInfo() *pb.User { return &pb.User{ Id: uuid.NewString(), Name: uuid.NewString(), Age: 33, RealName: uuid.NewString(), Date: "", //Date: time.Now().Format(time.DateTime), } } func main() { info := userInfo() fmt.Println("data.Src:", info) format := protojson.Format(info) fmt.Println("data.Format:", format) marshal, err := protojson.Marshal(info) if err != nil { panic(err) } fmt.Println("data.Marshal:", string(marshal)) user1 := &pb.User{} err = protojson.Unmarshal(marshal, user1) fmt.Println("data.Unmarshal:", user1) //marshalOpt := marshal2, err := protojson.MarshalOptions{ EmitUnpopulated: true, }.Marshal(info) if err != nil { panic(err) } fmt.Println("data.Marshal2:", string(marshal2)) user2 := &pb.User{} err = protojson.Unmarshal(marshal2, user2) fmt.Println("data.Unmarshal2:", user2) }
输出如下:
data.Src: id:"df8bbcca-d8b9-4e41-91ff-6ccf01567d27" name:"67115015-48bb-4284-b601-e9348a53d40f" age:33 real_name:"bed916f1-0fb3-413c-9de3-222cbc90c814" data.Format: { "id": "df8bbcca-d8b9-4e41-91ff-6ccf01567d27", "name": "67115015-48bb-4284-b601-e9348a53d40f", "age": 33, "realName": "bed916f1-0fb3-413c-9de3-222cbc90c814" } data.Marshal: {"id":"df8bbcca-d8b9-4e41-91ff-6ccf01567d27", "name":"67115015-48bb-4284-b601-e9348a53d40f", "age":33, "realName":"bed916f1-0fb3-413c-9de3-222cbc90c814"} data.Unmarshal: id:"df8bbcca-d8b9-4e41-91ff-6ccf01567d27" name:"67115015-48bb-4284-b601-e9348a53d40f" age:33 real_name:"bed916f1-0fb3-413c-9de3-222cbc90c814" data.Marshal2: {"id":"df8bbcca-d8b9-4e41-91ff-6ccf01567d27", "name":"67115015-48bb-4284-b601-e9348a53d40f", "age":33, "realName":"bed916f1-0fb3-413c-9de3-222cbc90c814", "date":""} data.Unmarshal2: id:"df8bbcca-d8b9-4e41-91ff-6ccf01567d27" name:"67115015-48bb-4284-b601-e9348a53d40f" age:33 real_name:"bed916f1-0fb3-413c-9de3-222cbc90c814"
上例中:
- 通过 Marshal 或 MarshalOptions.Marshal 函数将 protobuf 转换为 JSON 格式.
- 通过 Unmarshal 或 MarshalOptions.Unmarshal 函数将JSON 格式的数据转换为 protobuf 消息.
- MarshalOptions 提供了一些自定义选项,例如例子中 "EmitUnpopulated: true," 是否输出未设置的字段. 这里虽然user.Data=""(默认值),但还是输出了空字符。
更多的option参考
type MarshalOptions struct { pragma.NoUnkeyedLiterals // Multiline 指定封送拆收器是否应以缩进形式格式化输出,并将每个文本元素放在新行上。// 如果 Indent 是空字符串,则选择任意缩进。 Multiline bool // Indent 指定在多行格式化输出中使用的缩进字符集, 以便每个条目前面都有缩进,并且 // 以换行符结尾。如果非空,则 Multiline 被视为 true。 // 缩进只能由空格或制表符组成。 Indent string // AllowPartial 允许对缺少必填字段的消息进行封送 // 而不返回错误。如果AllowPartial 为 false(默认值), // 如果缺少任何必填字段,Marshal 将返回错误。 AllowPartial bool // UseProtoNames 在 JSON字段名称中使用 proto 字段名称而不是小驼峰命名。 UseProtoNames bool // UseEnumNumbers 将枚举值作为数字发出。 UseEnumNumbers bool // EmitUnpopulated 指定是否发出未填充的字段。//它不会 发出未填充的 oneof 字段或未填充的扩展字段。 // 未填充字段发出的 JSON 值如下: // ╔═══════╤════════════════════════════╗ // ║ JSON │ Protobuf field ║ // ╠═══════╪════════════════════════════╣ // ║ false │ proto3 boolean fields ║ // ║ 0 │ proto3 numeric fields ║ // ║ "" │ proto3 string/bytes fields ║ // ║ null │ proto2 scalar fields ║ // ║ null │ message fields ║ // ║ [] │ list fields ║ // ║ {} │ map fields ║ // ╚═══════╧════════════════════════════╝ EmitUnpopulated bool // 解析器用于在扩展 google.protobuf.Any // 消息时查找类型。如果为零,则默认使用 protoregistry.GlobalTypes。 Resolver interface { protoregistry.ExtensionTypeResolver protoregistry.MessageTypeResolver } }
性能对比 protojson VS encoding/json
创建example_test.go
package main import ( "encoding/json" "google.golang.org/protobuf/encoding/protojson" "testing" ) func BenchmarkProtoJson(b *testing.B) { gen := userInfo() for i := 0; i < b.N; i++ { protojson.Marshal(gen) } } func BenchmarkStdJson(b *testing.B) { gen := userInfo() for i := 0; i < b.N; i++ { json.Marshal(gen) } }
结论如下:
BenchmarkProtoJson BenchmarkProtoJson-4 230895 4556 ns/op BenchmarkStdJson BenchmarkStdJson-4 715443 1732 ns/op
总结
本文通过实践例子介绍Protojson库实现Protocol Buffers与JSON之间的转换,以及其和标准库encoding/json性能对比。总的来说,利用Google Protocol Buffers定制API协议,和采用Protojson解决传输格式转换。在分布式系统无论是Rpc还是Http的网络通信,相信Protojson可以发挥不错的表现。
以上就是Go语言使用protojson库实现Protocol Buffers与JSON转换的详细内容,更多关于Go Protocol Buffers与JSON转换的资料请关注脚本之家其它相关文章!