'Converting Protobuf3 with enum to JSON in Go

How can I convert grpc/protobuf3 message to JSON where the enum is represented as string?

For example, the protobuf message:

enum Level {
    WARNING = 0;
    FATAL = 1;
    SEVERE = 2;
    ...
}

message Http {
    string message = 1;
    Level level = 2;
}

Is converted by:

j, _ := json.MarshalIndent(protoMessage, "", "\t")

To:

{
    "message": "Hello world!",
    "level": 2,
}

I wish to get:

{
    "message": "Hello world!",
    "level": "SEVERE",
}

Thanks



Solution 1:[1]

I found out that I should use the protobuf/jsonpb package and not the standard json package.

so:

j, _ := json.MarshalIndent(protoMessage, "", "\t")

Should be:

m := jsonpb.Marshaler{}
result, _ := m.MarshalToString(protoMessage)

Update

As noted bellow, jsonpb is depricated and the new solution is to use protojson

Solution 2:[2]

I found some of these modules (jsonpb) to be deprecated. What worked for me was the google encoding version:

import "google.golang.org/protobuf/encoding/protojson"

jsonString := protojson.Format(protoMessage)

Solution 3:[3]

Level is not a string though, it is an emum. There are really only two choices I see.

  1. Write a custom marshaller that does this for you
  2. Generate code that does this for you.

For #2, gogoprotobuf has an extension (still marked as experimental) that let's you do exactly this:

https://godoc.org/github.com/gogo/protobuf/plugin/enumstringer and https://github.com/gogo/protobuf/blob/master/extensions.md

Solution 4:[4]

For my use case, I wanted to write it to a file. Using the most recent packages as of this date, this was as close to a regular encoding/json marshal as I could get.

I used the google.golang.org/protobuf/encoding/protojson package and the .ProtoReflect().Interface() methods of my protocol buffer data structure.

package main

import (
    "io/ioutil"
    "log"

    "google.golang.org/protobuf/encoding/protojson"

    "myproject/proto"
)

func main() {
    myProtoStruct := proto.MyType{}

    data, err := protojson.Marshal(myProtoStruct.ProtoReflect().Interface())
    if err != nil {
        log.Fatalf("Failed to JSON marhsal protobuf.\nError: %s", err.Error())
    }

    err = ioutil.WriteFile("my.proto.dat", data, 0600)
    if err != nil {
        log.Fatalf("Failed to write protobuf data to file.\nError: %s", err.Error())
    }

    log.Println("Written to file.")
}

Solution 5:[5]

When it comes to serialize the json object, this would be helpful.

var msg bytes.Buffer
m := jsonpb.Marshaler{}
err := m.Marshal(&msg, event)

msg.Bytes() converts msg to byte stream.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Evan Moran
Solution 3 sberry
Solution 4
Solution 5 wthrain