ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

protoreflect库介绍

2021-04-25 09:57:13  阅读:179  来源: 互联网

标签:string proto fmt Parser 介绍 descriptor protoreflect message


简介

它的作用正如它的名字,是一个用来反射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.

此存储库为 protobufgRPC提供了反射API。 protobuf 中反射的核心是descriptordescriptor本身就是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

注意:输出的字段名首字母都是小写的,例如userIdname,这是因为我在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
		}
	}
}

标签:string,proto,fmt,Parser,介绍,descriptor,protoreflect,message
来源: https://blog.csdn.net/csdner_lv/article/details/116117237

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有