google.protobuf.Any grpc 字段始终为 nil
google.protobuf.Any field with grpc is always nil
我在 golang 中使用 gRPC。我有一个非常简单的原型定义和一个 gRPC 服务。 proto 定义在 Endorsement 中有一个类型为 google/protobuf/any 的字段。 gRPC 服务无法将此字段映射到输入值,并且它总是初始化为 nil
原型定义:
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)
}
google/protobuf/any 类型应该如何对未知类型进行 Unmarshal?
m, err := e.Data.UnmarshalNew()
if err != nil {
log.Print("Error while unmarshaling the endorsement")
}
throws an error: s:"not found"
你知道google.protobuf.Any
是messageType,所以当你没有为它设置任何值时,它会是nil。
您必须使用您的结构解组数据,请参阅下面 protobuf
示例中的代码
// 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 {
...
}
或者我认为你可以将它与指针接口一起使用 (&interface{})
像这样:
d := &interface{}{}
if err := endorsement.Data.UnmarshalTo(d); err != nil {
...
}
来自包1 文档Unmarshaling an Any:
UnmarshalNew
uses the global type registry to resolve the message type and construct a new instance of that message to unmarshal into. In order for a message type to appear in the global registry, the Go type representing that protobuf message type must be linked into the Go binary. For messages generated by protoc-gen-go, this is achieved through an import of the generated Go package representing a .proto file.
类型注册表是 protoregistry.GlobalTypes
。类型查找是使用 Any.TypeUrl
字段完成的,该字段在编组原始消息时由 gRPC 客户端设置为具体类型的 url。
关于 Any
的令人困惑的细节是它可以是 任何 protobuf 消息,但是那个 protobuf 消息 必须在某处定义 .
您的 .proto
文件没有与您输入的 data
对象匹配的消息定义。可能这个 Data
消息是在其他地方定义的(不是在你自己的原型文件中),但无论如何你 必须导入生成消息所在的 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]interface{}
如果客户可以发送任何字面意思。
然后在你的处理函数中:
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")
}
}
我在 golang 中使用 gRPC。我有一个非常简单的原型定义和一个 gRPC 服务。 proto 定义在 Endorsement 中有一个类型为 google/protobuf/any 的字段。 gRPC 服务无法将此字段映射到输入值,并且它总是初始化为 nil
原型定义:
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)
}
google/protobuf/any 类型应该如何对未知类型进行 Unmarshal?
m, err := e.Data.UnmarshalNew()
if err != nil {
log.Print("Error while unmarshaling the endorsement")
}
throws an error: s:"not found"
你知道google.protobuf.Any
是messageType,所以当你没有为它设置任何值时,它会是nil。
您必须使用您的结构解组数据,请参阅下面 protobuf
示例中的代码 // 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 {
...
}
或者我认为你可以将它与指针接口一起使用 (&interface{}) 像这样:
d := &interface{}{}
if err := endorsement.Data.UnmarshalTo(d); err != nil {
...
}
来自包1 文档Unmarshaling an Any:
UnmarshalNew
uses the global type registry to resolve the message type and construct a new instance of that message to unmarshal into. In order for a message type to appear in the global registry, the Go type representing that protobuf message type must be linked into the Go binary. For messages generated by protoc-gen-go, this is achieved through an import of the generated Go package representing a .proto file.
类型注册表是 protoregistry.GlobalTypes
。类型查找是使用 Any.TypeUrl
字段完成的,该字段在编组原始消息时由 gRPC 客户端设置为具体类型的 url。
关于 Any
的令人困惑的细节是它可以是 任何 protobuf 消息,但是那个 protobuf 消息 必须在某处定义 .
您的 .proto
文件没有与您输入的 data
对象匹配的消息定义。可能这个 Data
消息是在其他地方定义的(不是在你自己的原型文件中),但无论如何你 必须导入生成消息所在的 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]interface{}
如果客户可以发送任何字面意思。
然后在你的处理函数中:
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")
}
}