protoreflect库介绍
2021/4/25 10:27:51
本文主要是介绍protoreflect库介绍,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
简介
它的作用正如它的名字,是一个用来反射proto
文件的库。
仓库原地址:https://github.com/jhump/protoreflect
来自官方的介绍
This repo provides reflection APIs for protocol buffers (also known as “protobufs” for short) and gRPC. The core of reflection in protobufs is the descriptor. A descriptor is itself a protobuf message that describes a
.proto
source file or any element therein. So a collection of descriptors can describe an entire schema of protobuf types, including RPC services.
此存储库为 protobuf
和gRPC
提供了反射API。 protobuf 中反射的核心是descriptor
。 descriptor
本身就是protobuf message
,它描述 proto
源文件或其中的元素。 因此,descriptor
的集合可以描述protobuf类型的整个架构,包括RPC服务。
使用方式
首先创建一个 protoparse.Parser
,利用这个 Parser 去 parse 你的 proto 文件,得到 desc.FileDescriptor
。之后根据 FileDescriptor 中的方法来提取出里面的 message
就好了。
包内的重要类型
Parser
parser
就是用来分析文件的
// Parser parses proto source into descriptors. type Parser struct { // ... } // parse 文件并得到该文件的 FileDescriptor func (p Parser) ParseFiles(filenames ...string) ([]*desc.FileDescriptor, error){ // ... }
FileDescriptor
它是对一个 proto 文件的描述。
它提供了很多 method,用于获取 proto 文件里的情况,函数的作用看名字就可以知道了。
目前我常用的就这四个 method。
GetMessageTypes() []*MessageDescriptor // 获取文件内所有的 message 类型 GetEnumTypes() []*EnumDescriptor // 获取文件内所有的 enum 类型 FindMessage(msgName string) *MessageDescriptor // 根据名字来获取 message 类型 FindEnum(enumName string) *EnumDescriptor // 根据名字来获取 enum 类型
MessageDescriptor
它是对一个 proto message 的描述,也提供了很多 method。
只列出我常用的:
GetName() string // message 的名字 GetFullyQualifiedName() string // message 的全限定名 AsDescriptorProto() *descriptor.DescriptorProto // AsDescriptorProto returns the underlying descriptor proto GetFields() []*FieldDescriptor // 获取 message 内的所有字段 GetNestedMessageTypes() []*MessageDescriptor // 获取在 message 内内嵌的 message GetNestedEnumTypes() []*EnumDescriptor FindFieldByName(fieldName string) *FieldDescriptor FindFieldByNumber(tagNumber int32) *FieldDescriptor FindFieldByJSONName(jsonName string) *FieldDescriptor
FieldDescriptor
是对 message 内 field(字段)的描述。
常用的方法:
GetName() string GetFullyQualifiedName() string AsEnumDescriptorProto() *descriptor.EnumDescriptorProto String() string GetValues() []*EnumValueDescriptor // 获取该枚举所有的枚举值 FindValueByName(name string) *EnumValueDescriptor FindValueByNumber(num int32) *EnumValueDescriptor
例子
demo1:打印出 proto 文件内所有的 message
// test.proto syntax = "proto3"; package test; enum Sex{ Man = 0; Woman = 1; } message Player{ int64 userId = 1; string name = 2; Sex sex = 3; repeated int64 friends = 4; } message Monster{ // 怪物等级 int32 level = 1; }
import ( "fmt" "github.com/jhump/protoreflect/desc/protoparse" ) func main() { var parser protoparse.Parser fileDescriptors, _ := parser.ParseFiles("./test.proto") // 因为只有一个文件,所以肯定只有一个 fileDescriptor fileDescriptor := fileDescriptors[0] for _, msgDescriptor := range fileDescriptor.GetMessageTypes() { fmt.Println(msgDescriptor.GetName()) for _, fieldDesc := range msgDescriptor.GetFields() { fmt.Println("\t", fieldDesc.GetType().String(), fieldDesc.GetName()) } fmt.Println() } for _, enumDescriptor := range fileDescriptor.GetEnumTypes() { fmt.Println(enumDescriptor.GetName()) for _, valueDescriptor := range enumDescriptor.GetValues() { fmt.Println("\t", valueDescriptor.GetName()) } fmt.Println() } }
// Output Player TYPE_INT64 userId TYPE_STRING name TYPE_ENUM sex TYPE_INT64 friends Monster TYPE_INT32 level Sex Man Woman
注意:输出的字段名首字母都是小写的,例如userId
,name
,这是因为我在proto
文件中给他们定义时,首字母都是小写的。(通过proto
文件最终生成的proto.go
文件字段名的首字母都是大写的)
demo2:为 proto message 生成其对应的 json 形式
package main import ( "encoding/json" "fmt" "github.com/golang/protobuf/protoc-gen-go/descriptor" "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/desc/protoparse" ) func main() { var parser protoparse.Parser fileDescriptors, _ := parser.ParseFiles("./test.proto") // 因为只有一个文件,所以肯定只有一个 fileDescriptor fileDescriptor := fileDescriptors[0] m := make(map[string]interface{}) for _, msgDescriptor := range fileDescriptor.GetMessageTypes() { m[msgDescriptor.GetName()] = convertMessageToMap(msgDescriptor) } bs, _ := json.MarshalIndent(m, "", "\t") fmt.Println(string(bs)) } func convertMessageToMap(message *desc.MessageDescriptor) map[string]interface{} { m := make(map[string]interface{}) for _, fieldDescriptor := range message.GetFields() { fieldName := fieldDescriptor.GetName() if fieldDescriptor.IsRepeated() { // 如果是一个数组的话,就返回 nil 吧 m[fieldName] = nil continue } switch fieldDescriptor.GetType() { case descriptor.FieldDescriptorProto_TYPE_MESSAGE: m[fieldName] = convertMessageToMap(fieldDescriptor.GetMessageType()) continue } m[fieldName] = fieldDescriptor.GetDefaultValue() } return m }
// Output { "Monster": { "level": 0 }, "Player": { "friends": null, "name": "", "sex": 0, "userId": 0 }, "Union": { "captain": { "friends": null, "name": "", "sex": 0, "userId": 0 } } }
这篇关于protoreflect库介绍的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享