【问题标题】:Is "google/protobuf/struct.proto" the best way to send dynamic JSON over GRPC?“google/protobuf/struct.proto”是通过 GRPC 发送动态 JSON 的最佳方式吗?
【发布时间】:2019-03-28 17:14:32
【问题描述】:

我编写了一个简单的 GRPC 服务器和一个调用服务器的客户端(都在 Go 中)。请告诉我使用 golang/protobuf/struct 是否是使用 GRPC 发送动态 JSON 的最佳方式。 在下面的示例中,之前我将Details 创建为map[string]interface{} 并对其进行序列化。然后我在 protoMessage 中以bytes 发送它,并在服务器端反序列化消息。

这是最好/最有效的方法,还是我应该在我的原型文件中将 Details 定义为结构?

下面是User.proto文件

syntax = "proto3";
package messages;
import "google/protobuf/struct.proto";

service UserService {
    rpc SendJson (SendJsonRequest) returns (SendJsonResponse) {}
}

message SendJsonRequest {
    string UserID = 1;
    google.protobuf.Struct Details = 2;
}

message SendJsonResponse {
    string Response = 1;
}

下面是client.go文件

package main
import (
    "context"
    "flag"
    pb "grpc-test/messages/pb"
    "log"
    "google.golang.org/grpc"
)

func main() {
    var serverAddr = flag.String("server_addr", "localhost:5001", "The server address in the format of host:port")
    opts := []grpc.DialOption{grpc.WithInsecure()}
    conn, err := grpc.Dial(*serverAddr, opts...)
    if err != nil {
        log.Fatalf("did not connect: %s", err)
    }
    defer conn.Close()

    userClient := pb.NewUserServiceClient(conn)
    ctx := context.Background()

    sendJson(userClient, ctx)
}

func sendJson(userClient pb.UserServiceClient, ctx context.Context) {
    var item = &structpb.Struct{
        Fields: map[string]*structpb.Value{
            "name": &structpb.Value{
                Kind: &structpb.Value_StringValue{
                    StringValue: "Anuj",
                },
            },
            "age": &structpb.Value{
                Kind: &structpb.Value_StringValue{
                    StringValue: "Anuj",
                },
            },
        },
    }

    userGetRequest := &pb.SendJsonRequest{
        UserID: "A123",
        Details: item,
    }

    res, err := userClient.SendJson(ctx, userGetRequest)
}

【问题讨论】:

    标签: json go protocol-buffers grpc protoc


    【解决方案1】:

    基于这个 proto 文件。

    syntax = "proto3";
    package messages;
    import "google/protobuf/struct.proto";
    
    service UserService {
        rpc SendJson (SendJsonRequest) returns (SendJsonResponse) {}
    }
    
    message SendJsonRequest {
        string UserID = 1;
        google.protobuf.Struct Details = 2;
    }
    
    message SendJsonResponse {
        string Response = 1;
    }
    

    我认为使用google.protobuf.Struct类型是一个很好的解决方案。

    回答的人一开始对我帮助很大,所以我想对你的工作表示感谢! :) 我非常感谢这两种解决方案! :) 另一方面,我想我找到了一个更好的来制作这些Structs

    Anuj 的解决方案

    这有点过于复杂,但它可以工作。

    var item = &structpb.Struct{
        Fields: map[string]*structpb.Value{
            "name": &structpb.Value{
                Kind: &structpb.Value_StringValue{
                    StringValue: "Anuj",
                },
            },
            "age": &structpb.Value{
                Kind: &structpb.Value_StringValue{
                    StringValue: "Anuj",
                },
            },
        },
    }
    

    卢克的解决方案

    这是一个较短的,但仍需要比必要更多的转换。 map[string]interface{} -> bytes -> Struct

    m := map[string]interface{}{
      "foo":"bar",
      "baz":123,
    }
    b, err := json.Marshal(m)
    s := &structpb.Struct{}
    err = protojson.Unmarshal(b, s)
    

    我认为的解决方案

    我的解决方案将使用来自structpb 包的官方函数,该包有很好的文档记录且用户友好。

    文档: https://pkg.go.dev/google.golang.org/protobuf/types/known/structpb

    例如,此代码通过设计用于执行此操作的函数创建*structpb.Struct

    m := map[string]interface{}{
        "name": "Anuj",
        "age":  23,
    }
    
    details, err := structpb.NewStruct(m) // Check to rules below to avoid errors
    if err != nil {
        panic(err)
    }
    
    userGetRequest := &pb.SendJsonRequest{
        UserID: "A123",
        Details: details,
    }
    

    当我们从map[string]interface{} 构建Struct 时,我们应该记住的最重要的事情之一是:

    https://pkg.go.dev/google.golang.org/protobuf/types/known/structpb#NewValue

    // NewValue constructs a Value from a general-purpose Go interface.
    //
    //  ╔════════════════════════╤════════════════════════════════════════════╗
    //  ║ Go type                │ Conversion                                 ║
    //  ╠════════════════════════╪════════════════════════════════════════════╣
    //  ║ nil                    │ stored as NullValue                        ║
    //  ║ bool                   │ stored as BoolValue                        ║
    //  ║ int, int32, int64      │ stored as NumberValue                      ║
    //  ║ uint, uint32, uint64   │ stored as NumberValue                      ║
    //  ║ float32, float64       │ stored as NumberValue                      ║
    //  ║ string                 │ stored as StringValue; must be valid UTF-8 ║
    //  ║ []byte                 │ stored as StringValue; base64-encoded      ║
    //  ║ map[string]interface{} │ stored as StructValue                      ║
    //  ║ []interface{}          │ stored as ListValue                        ║
    //  ╚════════════════════════╧════════════════════════════════════════════╝
    //
    // When converting an int64 or uint64 to a NumberValue, numeric precision loss
    // is possible since they are stored as a float64.
    

    例如,如果您想生成一个具有 JSON 形式的字符串列表的Struct,您应该创建以下map[string]interface{}

    m := map[string]interface{}{
        "name": "Anuj",
        "age":  23,
        "cars": []interface{}{
            "Toyota",
            "Honda",
            "Dodge",
        }
    }
    

    很抱歉这篇长文,我希望proto3Go 能让你的工作更轻松! :)

    【讨论】:

      【解决方案2】:

      我最终使用了protojson 的两步转换,从 map 到 json 到 struct:

      m := map[string]interface{}{
        "foo":"bar",
        "baz":123,
      }
      b, err := json.Marshal(m)
      s := &structpb.Struct{}
      err = protojson.Unmarshal(b, s)
      

      我不觉得它很优雅,但真的找不到任何官方文档来说明如何以不同的方式做到这一点。我也更喜欢使用“官方”功能生成结构,而不是尝试自己构建结构。

      【讨论】:

        【解决方案3】:

        如果您已经有 JSON 数据,您也可以选择将其编码为字符串字段。否则,使用 google.protobuf.Struct 似乎很合理,您应该能够在客户端和服务器上使用jsonpb 在 Struct 和 JSON 之间轻松转换。

        【讨论】:

        • 任何关于如何将jsobpb 用于原型结构类型的示例都会很有帮助
        • @Arajit 1) struct -> []byte // 使用 json.Unmarshal。 2) proto struct -> []byte // 使用 jsonpb 包; bts:= buffers.NewBuffer(nil) jsonpb.Unmarshaler{}.Marshal(bts, yourProto)。 3) []byte -> proto struct // 使用 jsonpb 包; jsonpb.Unmarshal(bytes.NewBuffer(bts), &protoStruct{})
        猜你喜欢
        • 2023-02-02
        • 1970-01-01
        • 1970-01-01
        • 2021-03-04
        • 1970-01-01
        • 2020-09-28
        • 1970-01-01
        • 1970-01-01
        • 2017-01-03
        相关资源
        最近更新 更多