'How to convert from Json to Protobuf?

I'm new to using protobuf, and was wondering if there is a simple way to convert a json stream/string to a protobuf stream/string in Java?

For example,

protoString = convertToProto(jsonString)

I have a json string that I want to parse into a protobuf message. So, I want to first convert the json string to protobuf, and then call Message.parseFrom() on it.

Thanks in advance for the help!



Solution 1:[1]

With proto3 you can do this using JsonFormat. It parses directly from the JSON representation, so there is no need for separately calling MyMessage.parseFrom(...). Something like this should work:

JsonFormat.parser().merge(json_string, builder);

Solution 2:[2]

//You can use this for converting your input json to a Struct / any other Protobuf Class    

import com.google.protobuf.Struct.Builder;
import com.google.protobuf.Struct;
import com.google.protobuf.util.JsonFormat;
import org.json.JSONObject;

JSONObject parameters = new JSONObject();

Builder structBuilder = Struct.newBuilder();
JsonFormat.parser().merge(parameters.toString(), structBuilder);

// Now use the structBuilder to pass below (I used it for Dialog Flow V2 Context Management)

Solution 3:[3]

online service: https://json-to-proto.github.io/

This tool instantly converts JSON into a Protobuf. Paste a JSON structure on the left and the equivalent Protobuf will be generated to the right, which you can paste into your program. The script has to make some assumptions, so double-check the output!

Solution 4:[4]

Since someone asked about getting the exception "com.google.protobuf.InvalidProtocolBufferException: JsonObject" when following Adam's advice--I ran into the same issue. Turns out it was due to the google protobuf timestamps. They are being serialized as an object containing two fields "seconds" and "nanos", since this isn't production code, I just got around this by parsing the JSON using jackson, going through the JSON object recursively and changing every timestamp from an object to a string formatted as per RFC 3339, I then serialized it back out and used the protobuf JSON parser as Adam has shown. This fixed the issue. This is some throwaway code I wrote (in my case all timestamp fields contain the word "timestamp", this could be more robust, but I don't care):

public Map<String, Object> fixJsonTimestamps(Map<String, Object> inMap) {
    Map<String, Object> outMap = new HashMap<>();
    for(String key : inMap.keySet()) {
        Object val = inMap.get(key);
        if(val instanceof Map) {
            Map<String, Object> valMap = (Map<String, Object>)val;
            if(key.toLowerCase().contains("timestamp") && 
                    valMap.containsKey("seconds") && valMap.containsKey("nanos")) {
                if(valMap.get("seconds") != null) {
                    ZonedDateTime d = ZonedDateTime.ofInstant(Instant.ofEpochSecond((int)valMap.get("seconds")).plusNanos((int)valMap.get("nanos")),
                            ZoneId.of("UTC"));
                    val = d.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));
                }
                
            } else {
                val = fixJsonTimestamps(valMap);
            }
        } else if(val instanceof List && ((List) val).size() > 0 && 
                ((List) val).get(0) instanceof Map) {
            List<Map<String, Object>> outputList = new ArrayList<>();
            for(Map item : (List<Map>)val) {
                outputList.add(fixJsonTimestamps(item));
            }
            val = outputList;
        }
        outMap.put(key, val);
    }
    return outMap;
}

Not the most ideal solution but it works for what I am doing, I think I saw someone recommend using a different timestamp class.

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 Adam Cozzette
Solution 2 Ayyub Kolsawala
Solution 3 Denis Orlov
Solution 4 Benjamin McCord