json - "google/protobuf/struct.proto"是通过 GRPC 发送动态 JSON 的最佳方式吗?

标签 json go protocol-buffers grpc protoc

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

这是最好/最有效的方法还是我应该在我的原型(prototype)文件中将 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)
}

最佳答案

基于这个原型(prototype)文件。

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 的工作更轻松! :)

关于json - "google/protobuf/struct.proto"是通过 GRPC 发送动态 JSON 的最佳方式吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52966444/

相关文章:

go - 将golang目录树打包成一个文件

protocol-buffers - 在 protobuf 消息中定义嵌套的 oneof 消息

protocol-buffers - 如何在 python 中编码 protobuf 映射?

javascript - 仅运行一次的 Chrome 扩展程序

javascript - 谷歌样本上的基本 Action ?

java - 为具有自己的 JSON 序列化实现的类编写 Gson TypeAdapter

unit-testing - 为什么 `go test -run NotExist` 通过?

Golang fsnotify 在 Windows 上为同一个文件发送多个事件

java - Protocol Buffer : get byte array from ByteString without copying

json - D3 map ,点击并从json中获取id