在grpc通信过程中,我们可能回传递Map<String,Object>对象。而在proto中,并没有Object类型,需要额外的进行处理。
在proto对象与Java对象的转换工具类中,可以借助于反射类来完成pb对象和java对象的转换,但如果pb对象中有map类型时,使用反射处理会抛出异常。需要额外的进行处理。
1. Map<String,Object>类型如何定义proto对象?
proto对象格式:
message PrimitiveObjectMessage {
oneof type {
string string_value = 1;
int64 long_value = 2;
int32 int_value = 3;
double double_value = 4;
float float_value = 5;
bool bool_value = 6;
int64 date_value = 7;
string json_value = 8;
NullValue null_value = 9;
ObjectArrayMessage array = 10;
ObjectArrayMessage list = 11;
}
}
enum NullValue {
NULL_VAL = 0;
}
message ObjectArrayMessage {
repeated PrimitiveObjectMessage values = 1;
}
message StringObjectMapMessage {
map<string, PrimitiveObjectMessage> value = 1;
}
message StringMapMessage {
map<string, string> value = 1;
}
如果业务对象想定义List<Map<String,Object>>
类型,则如下所示(注意map不能直接使用repeated
):
message busInfo {
repeated StringObjectMapMessage data_list =4;
}
2. Map<String,Object>如何完成pb和java的转换?
工具类:其本质就是通过for循环来遍历对象,完成数据的拷贝,但是需要额外注意的是我们自定义的PrimitiveObjectMessage
对象是Object类型,对不同的场景,需要进行额外的处理。
public class PbUtilsEx {
/**
* Map<String,String> 的proto--->java对象
*/
public static Map<String, String> toStringMap(StringMapMessage message) {
Map<String, String> valueMap = message.getValueMap();
return new HashMap<>(valueMap);
}
/**
* Map<String,String> 的java--->proto对象
*/
public static StringMapMessage.Builder fromStringMap(Map<String, String> map) {
StringMapMessage.Builder builder = StringMapMessage.newBuilder();
if (map != null) {
for (Map.Entry<String, String> entry : map.entrySet()) {
builder.putValue(entry.getKey(), entry.getValue());
}
}
return builder;
}
/**
* Map<String,Object> 的proto--->java对象
*/
public static Map<String, Object> toStringObjectMap(StringObjectMapMessage message) {
Map<String, Object> resultMap = new HashMap<>();
Map<String, PrimitiveObjectMessage> valueMap = message.getValueMap();
valueMap.forEach((key, value) -> resultMap.put(key, toPrimitiveObject(value)));
return resultMap;
}
/**
* Map<String,Object> 的java--->proto对象
*/
public static StringObjectMapMessage fromStringObjectMap(Map<String, Object> map) {
StringObjectMapMessage.Builder builder = StringObjectMapMessage.newBuilder();
if (map != null) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
builder.putValue(entry.getKey(), toPrimitiveObject(entry.getValue()));
}
}
return builder.build();
}
private static Object toPrimitiveObject(PrimitiveObjectMessage message) {
Object obj;
if (message.hasStringValue()) {
obj = message.getStringValue();
} else if (message.hasDoubleValue()) {
obj = message.getDoubleValue();
} else if (message.hasDateValue()) {
obj = new Date(message.getDateValue());
} else if (message.hasIntValue()) {
obj = message.getIntValue();
} else if (message.hasLongValue()) {
obj = message.getLongValue();
} else if (message.hasFloatValue()) {
obj = message.getFloatValue();
} else if (message.hasNullValue()) {
obj = null;
} else if (message.hasBoolValue()) {
obj = message.getBoolValue();
} else if (message.hasArray()) {
obj = toObjectList(message.getArray().getValuesList()).toArray();
} else if (message.hasList()) {
obj = toObjectList(message.getList().getValuesList());
} else {
obj = message.getJsonValue();
}
return obj;
}
private static List<Object> toObjectList(List<PrimitiveObjectMessage> messageList) {
List<Object> resultList = new ArrayList<>(messageList.size());
messageList.forEach((item) -> {
resultList.add(toPrimitiveObject(item));
});
return resultList;
}
private static PrimitiveObjectMessage toPrimitiveObject(Object obj) {
PrimitiveObjectMessage.Builder builder = PrimitiveObjectMessage.newBuilder();
if (obj instanceof String) {
builder.setStringValue((String) obj);
} else if (obj instanceof Double) {
builder.setDoubleValue((Double) obj);
} else if (obj instanceof Integer) {
builder.setIntValue((Integer) obj);
} else if (obj instanceof Short) {
builder.setIntValue(((Short) obj).intValue());
} else if (obj instanceof Long) {
builder.setLongValue((Long) obj);
} else if (obj instanceof BigDecimal) {
BigDecimal bigDecimal = (BigDecimal) obj;
if (bigDecimal.doubleValue() == (double) bigDecimal.intValue()) {
builder.setIntValue(bigDecimal.intValue());
} else if (bigDecimal.doubleValue() == (double) bigDecimal.longValue()) {
builder.setLongValue(bigDecimal.longValue());
} else {
builder.setDoubleValue(bigDecimal.doubleValue());
}
} else if (obj instanceof Date) {
builder.setDateValue(((Date) obj).getTime());
} else if (obj instanceof Float) {
builder.setFloatValue((Float) obj);
} else if (obj instanceof Boolean) {
builder.setBoolValue((Boolean) obj);
} else if (obj == null) {
builder.setNullValue(NullValue.NULL_VAL);
} else {
ObjectArrayMessage array;
if (obj instanceof Collection) {
array = fromObjectArray(((Collection<?>) obj).toArray());
builder.setList(array);
} else if (obj.getClass().isArray()) {
array = fromObjectArray(((Object[]) obj));
builder.setArray(array);
} else if (obj instanceof Number) {
builder.setDoubleValue(toDouble(obj));
} else {
builder.setJsonValue(ObjectMapperUtils.toJSON(obj));
}
}
return builder.build();
}
private static ObjectArrayMessage fromObjectArray(Object[] array) {
ObjectArrayMessage.Builder builder = ObjectArrayMessage.newBuilder();
Arrays.stream(array).forEach((obj) -> builder.addValues(toPrimitiveObject(obj)));
return builder.build();
}
}