当前位置: 首页 > 知识库问答 >
问题:

谷歌。protobuf。任何具有grpc的字段都始终为零

姚树
2023-03-14

我正在与golang一起使用gRPC。我有一个非常简单的原型定义和一个gRPC服务。proto定义中有一个类型为google/protobuf/any的字段。gRPC服务无法将此字段映射到输入值,它总是被初始化为零

原型定义:

syntax = "proto3";

option go_package = "service";
option java_multiple_files = true;
option java_package = "io.grpc.consensus";

import "google/protobuf/any.proto";

package service;

service MyService {
  rpc Verify (Payload) returns (Response) {}
}

message Response {
  string policyId =1;
  string txnId =2;
}

message Endorsement {
  string endorserId=1;
  // This is being initialise to nil by gRPC
  google.protobuf.Any data = 2;
  string signature=3;
  bool isVerified=4;
}


message Payload {
  string policyId =1;
  string txnId =2;
  repeated Endorsement endorsements=3;
}

利用此功能,可以实现一个简单的gRPC服务:

package service

import (
    "log"

    "golang.org/x/net/context"
)

type ServiceServerImpl struct {
}

func NewServiceServerImpl() *ServiceServerImpl {
    return &ServiceServerImpl{}
}

func (s *ServiceServerImpl) Verify(ctx context.Context, txnPayload *Payload) (*Response, error) {
    log.Printf("Got verification request: %s", txnPayload.TxnId)
    for _, endorsement := range txnPayload.Endorsements {
        j, err := endorsement.Data.UnmarshalNew()
        if err != nil {
            log.Print("Error while unmarshaling the endorsement")
        }
        if j==nil {
       //This gets printed as payload's endorsement data is always null for google/protobuf/any type
            log.Print("Data is null for endorsement")
        }
    }
    return &Response{TxnId: txnPayload.TxnId,  PolicyId: txnPayload.PolicyId}, nil
}

输入数据:

{
  "policyId": "9dd97b1e-b76f-4c49-b067-22143c954e75",
  "txnId": "231-4dc0-8e54-58231df6f0ce",
  "endorsements": [
    {
      "endorserId": "67e1dfbd-1716-4d91-94ec-83dde64e4b80",
      "data": {
        "type": "issueTx",
        "userId": 1,
        "transaction": {
            "amount": 10123.50
        }
    },
      "signature": "MEUCIBkooxG2uFZeSEeaf5Xh5hWLxcKGMxCZzfnPshOh22y2AiEAwVLAaGhccUv8UhgC291qNWtxrGawX2pPsI7UUA/7QLM=",
      "isVerified": false
    }
  ]
}

客户:

type Data struct {
    Type        string      `json:"type"`
    UserId      int16       `json:"userId"`
    Transaction Transaction `json:"transaction"`
}

type Transaction struct {
    Amount float32 `json:"amount"`
}

data := &Data{Type: "Buffer", UserId: 1, Transaction: Transaction{Amount: 10123.50}}
    byteData, err := json.Marshal(data)
    if err != nil {
        log.Printf("Could not convert data input to bytes")
    }

    e := &any.Any{
        TypeUrl: "anything",
        Value:   byteData,
    }

    endosement := &Endorsement{EndorserId: "1", Signature: "MEUCIBkooxG2uFZeSEeaf5Xh5hWLxcKGMxCZzfnPshOh22y2AiEAwVLAaGhccUv8UhgC291qNWtxrGawX2pPsI7UUA/7QLM=", Data: e, IsVerified: false}
    var endosements = make([]*Endorsement, 1)
    endosements[0] = endosement
    t := &Payload{TxnId: "123", PolicyId: "456", Endorsements: endosements}

    response, err := c.Verify(context.Background(), t)
    if err != nil {
        log.Fatalf("Error when calling SayHello: %s", err)
    }

对于未知类型,谷歌/原型/任何类型应该如何Unmarshal?

m, err := e.Data.UnmarshalNew()
    if err != nil {
        log.Print("Error while unmarshaling the endorsement")
    }

抛出错误:s:"not find"

共有2个答案

庄康胜
2023-03-14

正如你所知,google。protobuf。Any是messageType,因此当您没有为其设置任何值时,它将为nil。

你必须散集你的数据与你的结构体,请参阅下面的代码从示例的协议

      // marshal any
      foo := &pb.Foo{...}
      any, err := anypb.New(foo)
      if err != nil {
        ...
      }
      // unmarshal any 
      foo := &pb.Foo{}
      if err := any.UnmarshalTo(foo); err != nil {
        ...
      }

或者我想你可以把它和指针接口一起使用(

      d := &interface{}{}
      if err := endorsement.Data.UnmarshalTo(d); err != nil {
        ...
      }
曾飞沉
2023-03-14

从软件包1文档中解压任何:

UnmarshalNew使用全局类型注册表来解析消息类型,并构造该消息的新实例以散集到其中。为了使消息类型出现在全局注册表中,表示该原型消息类型的Go类型必须链接到Go二进制文件中。对于Procrc-gen-go生成的消息,这是通过导入表示. proto文件的生成的Go包来实现的。

类型注册表是原型注册表。GlobalTypes。类型查找是使用any. TypeUrl字段完成的,该字段由gRPC客户端在编组原始消息时设置为具体类型的url。

关于Any的令人困惑的细节是它可以是任何原型消息,但该原型消息必须在某个地方定义。

你的。proto文件没有与输入中的数据对象匹配的消息定义。这可能是因为该数据消息是在其他地方定义的(不是在您自己的proto文件中),但不管如何,您必须在生成消息的位置导入Go包。

否则,如果输入不是来自已经定义的原始消息,您可以自己将消息定义添加到原始消息中,然后使用UnmarshalTo

// proto file
message Data {
    string type = 1;
    int user_id = 2;
    Transaction transaction = 3;
}

message Transaction {
    float amount = 1;
}

然后:

    for _, endorsement := range txnPayload.Endorsements {
        data := generated.Data{}
        err := endorsement.Data.UnmarshalTo(&data)
        if err != nil {
            log.Print("Error while unmarshaling the endorsement")
        }
    }

如果您只需要一个任意的字节序列,即一个真正未知的类型,那么使用原始类型bytes并将其视为JSON有效负载。

将其建模为Go结构:

type Data struct {
    Type     string `json:"type"`
    UserID   int    `json:"userId"`
    Transaction struct{
        Amount float64 `json:"amount"`
    } `json:"transaction"`

}

或者作为map[string]接口{},如果客户端可以发送任何内容。

然后在处理函数中:

    for _, endorsement := range txnPayload.Endorsements {
        data := Data{} // or `map[string]interface{}`
        err := json.Unmarshal(endorsement.Data, &data)
        if err != nil {
            log.Print("Error while unmarshaling the endorsement")
        }
    }
 类似资料:
  • 问题内容: 我尝试@Inject这样的字段(它是一个jar模块,在META-INF下存在空bean.xml): IDataProvider接口 数据提供者实现import javax.enterprise.context.ApplicationScoped; 我尝试注入DataProvider的类 如果我在Wildfly上运行此命令,则注入的dataProvider始终为null(DataCont

  • 问题内容: 我想知道下面的代码是否有意义,因为编译器会警告“空白的最终字段对象可能尚未初始化”。有更好的方法吗? 问题答案: 我将字段定为final,并强制构造函数将值向上传递:

  • 我最近取了一个代表gRPC服务的proto文件,并从中生成了Java代码。然而,所有的接口都需要一个com.google.protobuf.的BlockingRpcChannel,我不知道如何创建它。当我看例子时,我看到人们使用io.grpc.ManagedChannel,但那是因为生成的Java代码使用了该类型。我不确定这是因为我使用了特定版本的原型还是什么? 以下是我所看到的例子https:/

  • 我试图理解protobuf和gRPC,以及如何使用这两种方法。你能帮我理解以下几点吗: 考虑到OSI模型,在哪里,例如Protobuf在第4层? 通过消息传输来思考“流”是怎样的,gRPC在做什么而protobuf错过了什么? 如果发送方使用protobuf,服务器是否可以使用gRPC,或者gRPC是否添加了只有gRPC客户端才能提供的内容? 如果gRPC可以使同步和异步通信成为可能,那么Prot

  • ProtoBuf 与 gRPC ProtoBuf 是一套接口描述语言(IDL)和相关工具集(主要是 protoc,基于 C++ 实现),类似 Apache 的 Thrift)。用户写好 .proto 描述文件,之后使用 protoc 可以很容易编译成众多计算机语言(C++、Java、Python、C#、Golang 等)的接口代码。这些代码可以支持 gRPC,也可以不支持。 gRPC 是 Goog

  • 问题内容: 我看到了我认为是错误的行为。@InjectMocks似乎并没有在每种测试方法之前创建一个新的测试主题。就像@Mock一样。在下面的示例中,如果Subject.section是最后一个,则@Test失败。如果不是最后两个都通过。我当前的解决方法是使用@BeforeClass,但这并不理想。 Subject.java: Section.java: SubjectTest.java 干杯。