zoukankan      html  css  js  c++  java
  • 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.

    此存储库为 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("	", fieldDesc.GetType().String(), fieldDesc.GetName())
    		}
    		fmt.Println()
    	}
    	for _, enumDescriptor := range fileDescriptor.GetEnumTypes() {
    		fmt.Println(enumDescriptor.GetName())
    		for _, valueDescriptor := range enumDescriptor.GetValues() {
    			fmt.Println("	", 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, "", "	")
    	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
    		}
    	}
    }
    
  • 相关阅读:
    往下滚动,导航栏隐藏
    判断是模拟器还是真机
    根据颜色生成图片
    UITextfiled 设置输入前面空格
    iOS 滑动TableView控制导航栏隐藏与显示
    时间 多少分钟前
    时间戳转时间
    iOS 常用公共方法(一)
    找工作感悟
    java 内存泄露
  • 原文地址:https://www.cnblogs.com/XiaoXiaoShuai-/p/14699174.html
Copyright © 2011-2022 走看看