Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert protocol-buffer message to a HashMap in java?

I have a protobuf message of the form

enum PolicyValidationType {
    Number = 0;
}


message NumberPolicyValidation {
    optional int64 maxValue = 1;
    optional int64 minValue = 2;
}

message PolicyObject {
    required string key = 1;
    optional string value = 2;
    optional string name = 3;
    optional PolicyValidationType validationType = 4;
    optional NumberPolicyValidation numberPolicyValidation = 5;
}

For example

policyObject {
      key: "sessionIdleTimeoutInSecs"
      value: "1800"
      name: "Session Idle Timeout"
      validationType: Number
      numberPolicyValidation {
        maxValue: 3600
        minValue: 5
      }
}

Can someone let me know how can I convert this to a Map like below:-

{validationType=Number, name=Session Idle Timeout, numberPolicyValidation={maxValue=3600.0, minValue=5.0}, value=1800, key=sessionIdleTimeoutInSecs}

One way I can think of is convert this to a json and then convert the json to map?

PolicyObject policyObject;
...
JsonFormat jsonFormat = new JsonFormat();
final String s = jsonFormat.printToString(policyObject);
Type objectMapType = new TypeToken<HashMap<String, Object>>() {}.getType();
Gson gson = new GsonBuilder().registerTypeAdapter(new TypeToken<HashMap<String,Object>>(){}.getType(), new PrimitiveDeserializer()).create();
Map<String, Object> mappedObject = gson.fromJson(s, objectMapType);

I think there must be some better way. Can someone suggest any better approach?

like image 255
tuk Avatar asked Feb 16 '18 07:02

tuk


Video Answer


1 Answers

I created small dedicated class to generically convert any Google protocol buffer message into a Java Map.

public class ProtoUtil {

@NotNull
public Map<String, Object> protoToMap(Message proto) {
    final Map<Descriptors.FieldDescriptor, Object> allFields = proto.getAllFields();
    Map<String, Object> map = new LinkedHashMap<>();
    for (Map.Entry<Descriptors.FieldDescriptor, Object> entry : allFields.entrySet()) {
        final Descriptors.FieldDescriptor fieldDescriptor = entry.getKey();
        final Object requestVal = entry.getValue();
        final Object mapVal = convertVal(proto, fieldDescriptor, requestVal);
        if (mapVal != null) {
            final String fieldName = fieldDescriptor.getName();
            map.put(fieldName, mapVal);
        }
    }
    return map;
}


@Nullable
/*package*/ Object convertVal(@NotNull Message proto, @NotNull Descriptors.FieldDescriptor fieldDescriptor, @Nullable Object protoVal) {
    Object result = null;
    if (protoVal != null) {
        if (fieldDescriptor.isRepeated()) {
            if (proto.getRepeatedFieldCount(fieldDescriptor) > 0) {
                final List originals = (List) protoVal;
                final List copies = new ArrayList(originals.size());
                for (Object original : originals) {
                    copies.add(convertAtomicVal(fieldDescriptor, original));
                }
                result = copies;
            }
        } else {
            result = convertAtomicVal(fieldDescriptor, protoVal);
        }
    }
    return result;
}


@Nullable
/*package*/ Object convertAtomicVal(@NotNull Descriptors.FieldDescriptor fieldDescriptor, @Nullable Object protoVal) {
    Object result = null;
    if (protoVal != null) {
        switch (fieldDescriptor.getJavaType()) {
            case INT:
            case LONG:
            case FLOAT:
            case DOUBLE:
            case BOOLEAN:
            case STRING:
                result = protoVal;
                break;
            case BYTE_STRING:
            case ENUM:
                result = protoVal.toString();
                break;
            case MESSAGE:
                result = protoToMap((Message) protoVal);
                break;
        }
    }
    return result;
}


}

Hope that helps! Share and enjoy.

like image 188
Zarnuk Avatar answered Sep 23 '22 17:09

Zarnuk