“ google / protobuf / struct.proto”是通过GRPC发送动态JSON的最佳方法吗?

Anu*_*pta 6 json go protocol-buffers grpc protoc

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

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

下面是client.go文件包的主要导入文件(“上下文”“标志” 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)
}
Run Code Online (Sandbox Code Playgroud)

F. *_*ert 19

基于这个 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;
}
Run Code Online (Sandbox Code Playgroud)

我认为使用该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",
            },
        },
    },
}
Run Code Online (Sandbox Code Playgroud)

卢克的解决方案

这是一个较短的,但仍然需要比必要更多的转换。 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)
Run Code Online (Sandbox Code Playgroud)

从我的角度来看解决方案

我的解决方案将使用structpb包中的官方功能,该功能有很好的文档记录和用户友好性。

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

例如,此代码创建了一个*structpb.Structvia 旨在执行此操作的函数。

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,
}
Run Code Online (Sandbox Code Playgroud)

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

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.
Run Code Online (Sandbox Code Playgroud)

例如,如果您想生成一个StructJSON 格式的字符串列表,您应该创建以下内容map[string]interface{}

m := map[string]interface{}{
    "name": "Anuj",
    "age":  23,
    "cars": []interface{}{
        "Toyota",
        "Honda",
        "Dodge",
    }
}
Run Code Online (Sandbox Code Playgroud)

很抱歉的长期职位,我希望它使您的工作更容易proto3Go!:)


Luk*_*uke 6

我最终使用了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)
Run Code Online (Sandbox Code Playgroud)

我不觉得它优雅,但真的找不到任何关于如何以不同方式执行此操作的官方文档。我也更喜欢使用“官方”功能生成结构,而不是尝试自己构建结构。


Dou*_*ley 3

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