lee cris
lee cris

Reputation: 21

How to use json as a structure member in protobuf or grpc?

When develop with javascript we always need to transport json with rpc. Now I want to use json as structure member of message in grpc. Please see below:

message HelloRequest{
    int32 hello = 1;
    json world = 2
} 

How to do that?

Upvotes: 2

Views: 9327

Answers (3)

Rajiv Singh
Rajiv Singh

Reputation: 3937

If you very specifically want that JSON structure and do not care about how pretty your proto files are, you can use the google.protobuf.Struct types to build a custom JSON structure.

https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/struct.proto

In this case, you probably want google.protobuf.ListValue.

Upvotes: 0

Long Tran
Long Tran

Reputation: 83

Thanks to the support from community, here I'd use google.protobuf.Struct or gogo/protobuf/types/struct.pb.go

import "google/protobuf/struct.proto";

message HelloRequest{
    int32 hello = 1;
    google.protobuf.Struct world = 2
}

I'm currently using go v1.16 and grpc-gateway v1.16. The protoc command to gen <your_file>.pb.go should add this if you prefer gogo protobuf to google protobuf (the xx.pb.go files located in pb folder) :

    --gogo_out=plugins=grpc,\
Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types,\
Mgoogle/protobuf/duration.proto=github.com/gogo/protobuf/types,\
Mgoogle/protobuf/empty.proto=github.com/gogo/protobuf/types,\
Mgoogle/protobuf/struct.proto=github.com/gogo/protobuf/types,\
Mgoogle/api/annotations.proto=github.com/gogo/googleapis/google/api,\
Mgoogle/protobuf/field_mask.proto=github.com/gogo/protobuf/types:\
pb \

Last but not least, I can provide an example in which I'm using gogo protobuf. Please take a look at the code below!

import (
     "github.com/gogo/protobuf/jsonpb"
     "github.com/gogo/protobuf/types"
)

.....
world := &types.Struct{}
helloRequest := &pb.HelloRequest{World: world}
bts := []byte(`{"status":200,"user":{"id":1,"name":"A","age":26,"phone":null,"skills":{"backend":"Golang","frontend":"React"},"hobbies":["coding","running"]}}`)
    
// Every pb are proto.Message so that we have to use jsonpb package 
// instead of json package
err := jsonpb.Unmarshal(bytes.NewBuffer(bts), world)
if err != nil {
    panic(err)
}

The response json should be:

{ 
  "hello": 0,
  "world": {
    "status": 200,
    "user": {
      "id": 1,
      "name": "A",
      "age": 26,
      "phone": null,
      "skills": {
        "backend": "Golang",
        "frontend": "React"
      },
      "hobbies": [
        "coding",
        "running"
      ]
    }
  }
}

Upvotes: 0

gmolau
gmolau

Reputation: 3005

There are two options to do this, depending on if you know the structure of your JSON in advance. If you do you can simply write a separate message for your JSON object and include that in your original message:

message SomeJSONMessage {
    // Attributes of your JSON here
}

message HelloRequest {
    int32 hello 1;
    SomeJSONMessage message = 2;
}

This is possible because every JSON type maps natively to a Protobuf type.

If you do not know the structure of your JSON in advance you would have to use a google.protobuf.Struct, which is basically a JSON without a defined structure:

message HelloRequest {
    int32 hello 1;
    google.protobuf.Struct message = 2;
}

This maps easily to well-known types in many programming languages (object in JS, dict in Python), but you won't have any guarantee about what attributes it contains.

Upvotes: 2

Related Questions