protobuf 与 Java 对象互转
2019-04-02 本文已影响0人
go4awalk
Java使用protobuf时,往往需要在Java对象(比如POJO)和protobuf message的Java对象间互转:
- protobuf接口需要传入message参数: 把POJO转成message, 作为接口参数;
- protobuf接口会返回message参数: 需要把message转成POJO, 供后续使用.
比如:
protobuf的message
message HelloRequest {
string name = 1;
int32 type = 2;
string location = 3;
bool flag = 4;
}
Java的POJO:
@Getter
@Setter
@ToString
public class HelloDTO {
private String name;
private Integer type;
private String location;
private Boolean flag;
private String message;
}
POJO -> Message
Hello.HelloRequest.Builder builder = Hello.HelloRequest.newBuilder();
builder.setFlag(true);
builder.setName("Name1");
builder.setLocation("Location1");
builder.setType(1);
Hello.HelloRequest request = builder.build();
Message -> POJO
Hello.HelloRequest request;
HelloDTO hello = new HelloDTO();
hello.setFlag(request.getFlag());
hello.setName(request.getName());
hello.setLocation(request.getLocation());
hello.setType(request.getType());
可以想象, 在protobuf定义的message很多的情况下,这些代码写起来费时费力, 还容易遗漏,有什么好的方法可以解决这个问题呢?
实际上POJO与PB Message之间的转换, 类似于POJO与Json之间的转换, 所以可以把上述过程简化:
message -> POJO: 对于POJO中任一属性,只有message中也有时, 才取出来设置.
/**
* ProtoBuffer object to POJO
*/
private static <T> T fromProtoBuffer(GeneratedMessageV3 pbObject, Class<T> modelClass) {
T model = null;
try {
model = modelClass.newInstance();
Field[] modelFields = modelClass.getDeclaredFields();
if (modelFields != null && modelFields.length > 0) {
for (Field modelField : modelFields) {
String fieldName = modelField.getName();
String name = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Class<?> fieldType = modelField.getType();
try {
Method pbGetMethod = pbObject.getClass().getMethod("get" + name);
Object value = pbGetMethod.invoke(pbObject);
Method modelSetMethod = modelClass.getMethod("set" + name, fieldType);
modelSetMethod.invoke(model, value);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return model;
}
POJO -> message: 对于message中任一属性,只有POJO中也有时, 才取出来设置.
/**
* POJO -> ProtoBuffer object
*/
private static <T> T toProtoBuffer(Object model, Class<T> pbClass) {
if (!GeneratedMessageV3.class.isAssignableFrom(pbClass)) {
throw new RuntimeException("Not ProtoBuffer message type");
}
T pbObject = null;
try {
Object pbBuilder = pbClass.getDeclaredMethod("newBuilder").invoke(null);
Field[] pbFields = pbClass.getDeclaredFields();
if (pbFields != null && pbFields.length > 0) {
for (Field pbField : pbFields) {
String fieldName = pbField.getName().substring(0, pbField.getName().length() - 1);
String name = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Class<?> fieldType = pbField.getType();
if (pbField.getType() == Object.class) {
fieldType = String.class;
}
try {
Method modelGetMethod = model.getClass().getMethod("get" + name);
Object value = modelGetMethod.invoke(model);
if (value != null) {
Method pbBuilderSetMethod = pbBuilder.getClass().getMethod("set" + name, fieldType);
pbBuilderSetMethod.invoke(pbBuilder, value);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
pbObject = (T) pbBuilder.getClass().getDeclaredMethod("build").invoke(pbBuilder);
} catch (Exception e) {
e.printStackTrace();
}
return pbObject;
}
以上代码仅为示例代码(demo), 实际使用时往往message和POJO分别都是嵌套的, 因此需要略有调整.