CrazyGreenHand
CrazyGreenHand

Reputation: 193

Protobuf map type JSON format uses string literal "key" and "value" not the actual values

I am trying to convert a protobuf object to JSON format using com.googlecode.protobuf.format.JsonFormat but the map type came out unexpected.

My message is like this

message Response {
   repeated Candidate candidates = 1;
   map<string, ErrorMessage> errors = 2;
}

message ErrorMessage {
   string message = 0;
   ErrorType type = 1;
}

enum ErrorType {
   ERROR = 0;
   WARNING = 1;
}

The issue is the JSON format of the Response object I created

Response response = ...
Return new ResponseEntity<>(new JsonFormat().printToString(response), HttpStatus.OK);

I expect the errors be formatted as a map keyed by the string value (of the map key)

...
"errors": {
  "someID" : {
      "message": "blah blah",
      "type": "ERROR"
  }
}

However the actual output is (I evaluated only the new JsonFormat().printToString(response) part in intellij)

...
"errors": {
  "key": "someID",
  "value": {
      "message": "blah blah",
      "type": "ERROR"
  }
}

I hope it's some small configuration I missed to make protobuf (or Jackson?) to be aware of the actual key value ? not using "key" and "value".

BTW, what's the point of having literal "key" and "value" field in a map type ? You can't do constituent lookup with it and you might just use a custom type/object.

Upvotes: 1

Views: 4106

Answers (1)

madhead
madhead

Reputation: 33442

This code works perfectly for me:

test.proto

syntax = "proto2";
package by.dev.madhead;
option java_outer_classname = "SO";

message Candidate {

}

enum ErrorType {
    ERROR = 0;
    WARNING = 1;
}

message ErrorMessage {
    required string message = 1;
    required ErrorType type = 2;
}

message Response {
    repeated Candidate candidates = 1;
    map<string, ErrorMessage> errors = 2;
}

App.java

public class App {
    public static void main(String[] args) throws InvalidProtocolBufferException {
        SO.Response response = SO.Response.newBuilder()
            .addCandidates(SO.Candidate.newBuilder().build())
            .addCandidates(SO.Candidate.newBuilder().build())
            .addCandidates(SO.Candidate.newBuilder().build())
            .putErrors("error1", SO.ErrorMessage.newBuilder().setMessage("error1").setType(SO.ErrorType.ERROR).build())
            .putErrors("error2", SO.ErrorMessage.newBuilder().setMessage("error2").setType(SO.ErrorType.WARNING).build())
            .build();

        System.out.println(JsonFormat.printer().print(response));
    }
}

The output is:

{
    "candidates": [{
    }, {
    }, {
    }],
    "errors": {
        "error1": {
            "message": "error1",
            "type": "ERROR"
        },
        "error2": {
            "message": "error2",
            "type": "WARNING"
        }
    }
}

Which has no keys and value as you see. Make sure that you printed not the message itself, but the result of JsonFormat.printer().print(). Basically, key and values you've seen are from internal toString() implementation of Protobuf Message.

And the full class name for JsonFormat is com.google.protobuf.util.JsonFormat, not com.googlecode.protobuf.format.JsonFormat.

Upvotes: 1

Related Questions