proto与json互相转换(使用反射)

2019-11-22 00:13:54 浏览数 (1)

有时,需要动态的根据proto文件来构建一个proto对象。此时,就该反射库上场了。

代码语言:javascript复制
package jsonpb

import (
    "github.com/golang/protobuf/proto"
    "github.com/golang/protobuf/ptypes"
    "github.com/jhump/protoreflect/desc"
    "github.com/jhump/protoreflect/desc/protoparse"
    "github.com/jhump/protoreflect/dynamic"
    "github.com/rfyiamcool/grpcall"
    "google.golang.org/grpc/metadata"
    "google.golang.org/grpc/status"
    "sync"
)

var globalProtoMap map[string]*desc.FileDescriptor
var IsCached = true
var lk sync.RWMutex

func init() {
    globalProtoMap = make(map[string]*desc.FileDescriptor)
}

func getProto(path string) *desc.FileDescriptor {
    lk.Lock()
    defer lk.Unlock()

    if IsCached {
        fd, ok := globalProtoMap[path]
        if ok {
            logging.Debugf("getProto path:%v cached", path)
            return fd
        }
    }
    p := protoparse.Parser{
    }
    fds, err := p.ParseFiles(path)
    if err != nil {
        logging.Errorf("getProto ParseFiles error:%v", err)
        return nil
    }
    //logging.Debugf("JsonToPb fd %v, err %v", fds[0], err)
    fd := fds[0]

    if IsCached {
        globalProtoMap[path] = fd
    }

    return fd
}

// JsonToPb 传入proto文件的path, proto中对应的message.name,js的原始数据
// 返回生成的proto.Marshal的[]byte
// example:
// path := "$PROTOPATH/helloworld.proto"
// messageName "helloworld.HelloRequest"
// JsonToPb(path,"helloworld.HelloRequest", []byte(`{"name":"yzh"}`))
func JsonToPb(protoPath, messageName string, jsonStr []byte) ([]byte, error) {
    logging.Debugf("JsonToPb protoPath %v", protoPath)

    fd := getProto(protoPath)

    msg := fd.FindMessage(messageName)

    dymsg := dynamic.NewMessage(msg)
    err := dymsg.UnmarshalJSON(jsonStr)
    if err != nil {
        logging.Errorf("JsonToPb UnmarshalJSON error:%v", err)
        return nil, nil
    }
    logging.Debugf("JsonToPb UnmarshalJSON dymsg %v", dymsg)

    any, err := ptypes.MarshalAny(dymsg)
    if err != nil {
        logging.Errorf("JsonToPb MarshalAny error:%v", err)
        return nil, nil
    }
    logging.Debugf("JsonToPb marshal any %v", any.Value)
    return any.Value, nil
}

// PbToJson 传入proto的byte数据,返回它对应的json数据
// example:
// path := "$PROTOPATH/helloworld.proto"
// messageName "helloworld.HelloRequest"
// jsonByte, err := PbToJson(path, messageName, pbByte)
func PbToJson(protoPath, messageName string, protoData []byte) ([]byte, error) {
    logging.Debugf("PbToJson protoPath %v", protoPath)
    fd := getProto(protoPath)
    msg := fd.FindMessage(messageName)
    dymsg := dynamic.NewMessage(msg)

    err := proto.Unmarshal(protoData, dymsg)
    logging.Debugf("PbToJson Unmarshal err:%v", err)

    jsonByte, err := dymsg.MarshalJSON()
    return jsonByte, err
}

0 人点赞