func (m *TodoServer) GetTodos(ctx context.Context, empty *emptypb.Empty) (*desc.GetTodosResponse, error) {
todos, err := m.todoService.GetTodos()
if err != nil {
return nil, err
}
todosResp := make([]*desc.GetTodosResponse_Todo, 0, len(todos))
for _, todo := range todos {
todosResp = append(todosResp, &desc.GetTodosResponse_Todo{
Id: todo.ID,
Title: todo.Title,
IsCompleted: todo.IsCompleted,
})
}
return &desc.GetTodosResponse{Todos: todosResp}, nil
}
service TodoService {
rpc GetTodos(google.protobuf.Empty) returns (GetTodosResponse) {}
}
message GetTodosResponse {
repeated Todo todos = 1;
message Todo {
int64 id = 1;
string title = 2;
bool is_completed = 3;
}
}
I have one record in the db | id | title | is_completed | |-|-|-| | 1 | aaa | false |
the function above returns {"todos": [{"id": "1", "title": "aaa"}]}
but once I change the is_completed
to true
the result is correct {"todos": [{"id": "1", "title": "aaa", "isCompleted": true}]}
This is by design and for efficiency.
The "zero" value for a bool
is false
- so when initializing your protobuf
struct with a false
value, the field does not need to be explicitly stated when using the standard libary's encoding/json
unmarshaler. On the encoding end, if the field's JSON tag includes an omitempty
qualifier, the standard libary's encoding/json
marshaler will remove any zero values - which is what you are seeing.
You will see the same behavior with the Title
string field if it was ""
(i.e. the zero value of a string).
Looking at your generated code (*.pb.go
) the struct's bool
field definition will look something like this:
type Todo struct {
// ...
IsCompleted bool `protobuf:"varint,5,opt,name=is_complete,proto3" json:"is_complete,omitempty"`
}
So the json:"...,omitempty"
instructs the encoding/json
marshaler to omit any zero values during marshalling with tags like theses.
If you want to override this behavior:
omitempty
directive from your generated code (not recommended - as edits would need to be managed over the lifecycle of development). But if you must, refer to this answer;grpc-gateway
, override this at runtime e.g.gwmux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{OrigName: true, EmitDefaults: true}))
encoding/json
) use the JSON
marshaler from this package "google.golang.org/protobuf/encoding/protojson"
:protojson.Marshaler{EmitDefaults: true}.Marshal(w, resp)
as outlined in this answer.