14

I am new to grpc and have been trying to just fetch a json response from a webserver. The stub can then request the json from the rpc server.

In my .proto file, I created a message type:

message Post {
    int64 number = 1;
    string now = 2;
    string name = 3;
}

But I am not able to marshal the number field, since protoc produces the struct pb.go file with a number tag:

{
        "no": "23",
        "now": "12:06:46",
        "name": "bob"
}

How can I force the Message to be 'converted' with a tag other than the lowercase name of the message field? Such as using the json tag no, even if the field name in the Message is number.

3 Answers 3

17

You can set a proto3 field option on the proto message definition with a json_name

message Post {
    int64 number = 1 [json_name="no"];
    string now = 2;
    string name = 3;
}

link to the docs

Sign up to request clarification or add additional context in comments.

3 Comments

I have tried this, the compiled struct field will then have the following tags: Number string protobuf:"bytes,5,opt,name=no,json=com,proto3" json:"number,omitempty"``
The json_name option in the .proto file has nothing to do with the generated json tag, but rather with the name used in the official protobuf-to-JSON mapping: developers.google.com/protocol-buffers/docs/proto3#json, which is implemented in the jsonpb package. reference
it sucks one cannot actually define the real json tags.
13
import "github.com/gogo/protobuf/gogoproto/gogo.proto";

// Result example:
// type Post struct {
//    Number int64 `protobuf:"bytes,1,opt,name=number,json=no1,proto3" json:"no2"`
// }
message Post {
    int64 number = 1 [json_name="no1", (gogoproto.jsontag) = "no2"];
}

,where:

  • no1 - new name for jsonpb marshal/unmarshal
  • no2 - new name for json marshal/unmarshal

jsonpb example:

import (
    "bytes"
    "testing"
    "encoding/json"

    "github.com/golang/protobuf/jsonpb"
    "github.com/stretchr/testify/require"
)

func TestJSON(t *testing.T) {
    msg := &Post{
        Number: 1,
    }

    buf := bytes.NewBuffer(nil)

    require.NoError(t, (&jsonpb.Marshaler{}).Marshal(buf, msg))
    require.Equal(t, `{"no1":1}`, buf.String())

    buf.Truncate(0)

    require.NoError(t, json.NewEncoder(buf).Encode(msg))
    require.Equal(t, `{"no2":1}`, buf.String())
}

More information about protobuf extensions

Comments

0

Here is my solution, which is an friendly and compatible way for adding go struct tags in protobuf/grpc.

  1. Fork protobuf-go repo to your own github account, and add a go tags feature into cmd/protoc-gen-go. Like this: https://github.com/hacksomecn/protobuf-go/commit/2443a0ee4696acaa9aa4bf8c2e0586d7c724c645

  2. Install new-feature protoc-gen-go into you path. Like: go install github.com/hacksomecn/protobuf-go/cmd/protoc-gen-go@"v1.28.0-gotags"

  3. Declare message field with an tailing comment, add go tags expr in comment. Go tags expr regexp format is (\s?)@go_tags\(` + "(`.*`)" + `\)\s. Like:

message HelloGoTags {
  string Name = 1; // @go_tags(`json:"name,omitempty" yaml:"name" bson:"name" db:"name" gorm:"name" validate:"required"`) awesome name
}
  1. Use protoc compile .proto. Like:
protoc --go_out=. --go_opt=paths=source_relative  tags.proto
  1. Message HelloGoTags will be generated with extra tags. Like:
type HelloGoTags struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"name,omitempty" gorm:"name" validate:"required" yaml:"name" bson:"name" db:"name"` // awesome name
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.