I have a requirement to convert a POJO object to its corresponding protobuf message. I am writing one utility which will use reflection and generate the protobuf message.
I have below POJO models -
public class Person {
int id;
Map<String,String> properties;
String name;
Address address;
Gender gender;
}
public class Address {
private String line1;
private String line2;
private String line3;
private String City;
private String state;
}
public enum Gender {
MALE,FEMALE
}
Corresponding proto messages -
message PersonMsg {
int32 id = 1;
map<string, string> properties = 2;
string name = 3;
AddressMsg address = 4;
Gender gender = 5;
}
message AddressMsg {
string line1 = 1;
string line2 = 2;
string line3 = 3;
string city = 4;
string state = 5;
string country = 6;
string pincode = 7;
}
enum Gender{
Gender_DEFAULT = 0;
Gender_MALE = 1;
Gender_FEMALE = 2;
}
The converter logic is like - for scalar/primitive types dynamically invoke the gette/setters and populate the data in the protobuf builder. If fields are custom type (in this case Address field), then recursively call the method and populate the field -
private static GeneratedMessageV3 getMessage(Object o) throws ClassNotFoundException
String protoName = o.getClass().getName()+"Proto";
Class outerClass = Class.forName(protoName);
String msgName = protoName+"$"+ o.getClass().getSimpleName()+"Msg";
Class modelClass = o.getClass();
Class protoMsg = Class.forName(msgName);
try {
Method method = protoMsg.getMethod("newBuilder", null);
com.google.protobuf.GeneratedMessageV3.Builder builder = (com.google.protobuf.GeneratedMessageV3.Builder)method.invoke(null, null);
Field[] fields = o.getClass().getDeclaredFields();
for(Field field : fields){
if(field.getType().isPrimitive() || field.getType().isAssignableFrom(String.class)){
Method getMethod = modelClass.getMethod("get"+ CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field.getName()));
Object value = getMethod.invoke(o, null);
Method setMethod = findSetterMethod(builder.getClass(), field);
setMethod.invoke(builder, value);
} else if(field.getType().isEnum()){
Method getMethod = modelClass.getMethod("get"+ CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field.getName()));
Enum modelEnumValue = (Enum) getMethod.invoke(o, null);
String protoEnumClassName = field.getType().getName()+"Proto$"+field.getType().getSimpleName();
Class protoEnumClass = Class.forName(protoEnumClassName);
Method enumMethod = protoEnumClass.getDeclaredMethod("values", null);
Enum[] protoValues = (Enum[]) enumMethod.invoke(null, null);
Enum protoValue = null;
for(Enum value : protoValues){
if(value.name().replace(field.getType().getSimpleName()+"_", "").equals(modelEnumValue.name())){
protoValue = value;
}
}
if(protoValue!=null) {
Method setMethod = findSetterMethod(builder.getClass(), field);
setMethod.invoke(builder, protoValue);
}
} else if(Collection.class.isAssignableFrom(field.getType()) || Map.class.isAssignableFrom(field.getType())){
if(Map.class.isAssignableFrom(field.getType())){
populateMap(o, field, builder);
}
} else{
System.out.println("Field is custom type :"+ field.getName());
Method getMethod = modelClass.getMethod("get"+ CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field.getName()));
System.out.println("Found Getter Method :"+ getMethod);
Object modelValue = getMethod.invoke(o, null);
System.out.println("Model Value :"+ modelValue);
Method setMethod = findSetterMethod(builder.getClass(), field);
setMethod.invoke(builder, getMessage(modelValue));
}
}
GeneratedMessageV3 msg = (GeneratedMessageV3) builder.build();
return msg;
} catch (NoSuchMethodException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}```
This code works fine and convert the projo objects to protobuf message. But sometimes it fails at runtime with - "java.lang.IllegalArgumentException: argument type mismatch"
This is failing at line - "setMethod.invoke(builder, getMessage(modelValue));"
Any idea why this is failing if code is not changed at all ? How can i get rid/workaround this exception ?
Aucun commentaire:
Enregistrer un commentaire