io.bitsensor.lib.entity.util.ProtoUtils Maven / Gradle / Ivy
package io.bitsensor.lib.entity.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.bitsensor.lib.entity.DataModelException;
import io.bitsensor.lib.jackson.protobuf.ProtobufDeserializer;
import io.bitsensor.lib.jackson.protobuf.ProtobufModule;
import io.bitsensor.proto.shaded.com.google.protobuf.InvalidProtocolBufferException;
import io.bitsensor.proto.shaded.com.google.protobuf.MessageOrBuilder;
import io.bitsensor.proto.shaded.com.google.protobuf.util.JsonFormat;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import static io.bitsensor.lib.entity.Constants.*;
import static io.bitsensor.lib.entity.util.InputConverter.unflatten;
/**
* Utility class for working with proto messages.
*/
public class ProtoUtils {
/**
* Fields to exclude when unflattening object.
*
* This is necessary for converting json string into protobuf message as message can have nested messages.
*/
public static final String[] UNFLATTEN_EXCLUDED_FIELDS = {CONTEXT, ENDPOINT, META, INPUT};
private static final ObjectMapper mapper = ProtoUtils.objectMapper();
/**
* Returns new instance of ObjectMapper with protobuf module registered.
*
* @return object mapper
*/
public static ObjectMapper objectMapper() {
return new ObjectMapper().registerModule(new ProtobufModule(
JsonFormat.printer()
.omittingInsignificantWhitespace()
.includingDefaultValueFields()
.preservingProtoFieldNames(),
JsonFormat.parser()
.ignoringUnknownFields()
));
}
public static String getValueForKey(String key, JsonObject dataPoint) {
//Get type, typically Context, Endpoint, eg. and update key accordingly
String type, path;
try {
type = key.split("\\.")[0];
path = key.substring(type.length() + 1);
} catch (Exception e) {
throw new IllegalArgumentException();
}
try {
return dataPoint.get(type).getAsJsonObject()
.get(path).getAsString();
} catch (NullPointerException | IllegalStateException notInDataPointException) {
throw new DataModelException(notInDataPointException);
}
}
/**
* Returns json string converted from a proto message.
*
* @param object a proto message
* @return json string
* @throws DataModelException when a protocol message being parsed is invalid in some way
*/
public static String proto2String(MessageOrBuilder object) {
try {
return JsonFormat.printer()
.includingDefaultValueFields()
.omittingInsignificantWhitespace()
.preservingProtoFieldNames()
.print(object);
} catch (InvalidProtocolBufferException e) {
throw new DataModelException("Unable to print MessageOrBuilder object", e);
}
}
/**
* Returns json object converted from a proto message.
*
* @param object a proto message
* @return json object
* @throws DataModelException when a protocol message being parsed is invalid in some way
*/
public static JsonObject proto2Json(MessageOrBuilder object) {
return new JsonParser().parse(proto2String(object)).getAsJsonObject();
}
/**
* Returns json object without hashes properties converted from a proto message.
*
* @param object a proto message
* @return json object
* @throws DataModelException when a protocol message being parsed is invalid in some way
*/
public static JsonObject proto2JsonWithoutHashes(MessageOrBuilder object) {
JsonObject json = new JsonParser().parse(proto2String(object)).getAsJsonObject();
json.remove("hash");
json.remove("ruleHash");
return json;
}
/**
* Returns json string compatible with elasticsearch converted from a proto message.
*
* @param object a proto message
* @return json string
* @throws DataModelException when a protocol message being parsed is invalid in some way
*/
public static String proto2EsDocument(MessageOrBuilder object) {
return unflatten(proto2String(object)).toString();
}
/**
* Returns a JSON deserialized java object from a given json element.
*/
public static T convert(JsonElement element, Class type) {
return convert(element.toString(), type);
}
/**
* Returns a JSON deserialized java object from given a json string.
*/
public static T convert(String json, Class type) {
try {
return mapper.readValue(json, type);
} catch (IOException e) {
throw new DataModelException("Unable to parse Datapoint from json '" + json + "'", e);
}
}
/**
* Returns a proto-compatible json object used for deserializing proto message.
*
* @see ProtobufDeserializer
*/
public static JsonObject toProtoJson(String json, String... exludedFields) {
JsonObject object = unflatten(json, exludedFields);
// Filter out null value or JsonObject value with an array.
for (Map.Entry entry : object.entrySet()) {
if (!entry.getValue().isJsonObject())
continue;
final Iterator> each = entry.getValue().getAsJsonObject().entrySet().iterator();
while (each.hasNext()) {
if (each.next().getValue().isJsonNull()) {
each.remove();
}
}
}
// Filter out any array object in 'meta' field because it is not compatible with the datapoint schema. (meta -> map)
if (object.has(META)) {
final Iterator> each = object.getAsJsonObject(META).entrySet().iterator();
while (each.hasNext()) {
if (each.next().getValue().isJsonArray()) {
each.remove();
}
}
}
return object;
}
}