Go语言使用protojson库实现Protocol Buffers与JSON转换

 更新时间:2023年09月25日 08:44:02   作者: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转换的资料请关注脚本之家其它相关文章!

相关文章

  • Go 不支持 []T转换为[]interface类型详解

    Go 不支持 []T转换为[]interface类型详解

    这篇文章主要为大家介绍了Go不支持[]T转换为[]interface类型详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • 数据竞争和内存重分配Golang slice并发不安全问题解决

    数据竞争和内存重分配Golang slice并发不安全问题解决

    这篇文章主要为大家介绍了数据竞争和内存重分配Golang slice并发不安全问题解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • golang版本升级的简单实现步骤

    golang版本升级的简单实现步骤

    个人感觉Go在众多高级语言中,是在各方面都比较高效的,下面这篇文章主要给大家介绍了关于golang版本升级的简单实现步骤,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-02-02
  • Golang中Gin数据库表名前缀的三种方法

    Golang中Gin数据库表名前缀的三种方法

    本文主要介绍了Golang中Gin数据库表名前缀的三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-02-02
  • Go方法接收者值接收者与指针接收者详解

    Go方法接收者值接收者与指针接收者详解

    这篇文章主要为大家介绍了Go方法接收者值接收者与指针接收者详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Go-ethereum 解析ethersjs中产生的签名信息思路详解

    Go-ethereum 解析ethersjs中产生的签名信息思路详解

    这篇文章主要介绍了Go-ethereum 解析ethersjs中产生的签名信息,我们解析签名的需要知道,签名的消息,签名,和公钥,按照这个思路,我们可以通过ethers实现消息的签名,也可以通过go-ethereum实现,需要的朋友可以参考下
    2022-08-08
  • golang中slice扩容的具体实现

    golang中slice扩容的具体实现

    Go 语言中的切片扩容机制是 Go 运行时的一个关键部分,它确保切片在动态增加元素时能够高效地管理内存,本文主要介绍了golang中slice扩容的具体实现,感兴趣的可以了解一下
    2025-05-05
  • golang实现协程池的方法示例

    golang实现协程池的方法示例

    本文主要介绍了golang实现协程池的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-02-02
  • go如何删除字符串中的部分字符

    go如何删除字符串中的部分字符

    这篇文章主要介绍了go删除字符串中的部分字符操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Go语言中的IO操作及Flag包的用法

    Go语言中的IO操作及Flag包的用法

    这篇文章介绍了Go语言中的IO操作及Flag包的用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07

最新评论