Sometimes you'd like more than one way to serve an API. In my case I'm currently working on serving both
gRPC and HTTP. I'd like to have one place where objects are defined and have a nice way to serialize both from
protobuf (which is the serialization gRPC uses) and JSON .
When producing Go code, protobuf adds JSON struct tags. However since JSON comes from dynamic languages, fields can have any type. In Go we can use
map[string]interface{} but in protobuf this is a bit more complicated and we need to use
oneof. The struct generated by
oneof does not look like regular JSON and will make users of the API write complicated JSON structures.
What's nice about Go, is that we can have any type implement
json.Marshaler and
json.Unmarshaler. What's extra nice is that in Go, you can add these methods to the generated structs in another file (in Python, we'd have to change the generated source code since methods need to be inside the class definition).
Let's have a look at a simple Job definition
And now we can add some helper methods to aid with JSON serialization (protoc generates code to pb directory)
As a bonus, we added
job.Properties that returns a "native"
map[string]interface{}
Let's look at a simple example on how we can use it
And its output:
$ go run job.go
[j1] user:"Saitama" count:1 properties: > properties: >
[json] {"user":"Saitama","count":1,"properties":{"retries":3,"target":"Metal Knight"}}
[j2] user:"Saitama" count:1 properties: > properties: >