Answer the question
In order to leave comments, you need to log in
Universal transparent converter?
There was a need to create a transparent converter for complex objects of the same type in structure, but different in namespace. Initially, there are two XSDs with descriptions of different types. JAXB handles them perfectly and creates stubs. Further, according to the business logic, it is necessary to convert the object from one namespace to another. When there are not many types of objects, it is not very tedious to make an individual converter, but as the data structure grows, this approach is no longer effective. Therefore, the idea arose to write one universal converter with transparent data transfer.
I want to make the converter call in the program code look something like this:
MyObjectA _a = new MyObjectA();
...
MyObjectB _b = Transparent.convert(_a);
public class Transparent {
public static <A, B> A convert(B x) {
A res = null;
for (Method m : x.getClass().getMethods()) { // Читаем все методы полученного объекта из входного параметра
String _methodName = m.getName(); // Запоминаем название метода
if (_methodName.startsWith("get")) { // Если это getter, то проходим дальше
String _fieldName = _methodName.substring(3); // Отрезаем 'get' и запоминаем название поля
Class[] _paramTypes = new Class[]{m.getReturnType()}; // Запоминаем тип возвращаемого объекта этого метода
try {
Method _methodName2 = res.getClass().getMethod("set".concat(_fieldName ), _paramTypes);
// Пытаемся найти setter-метод с требуемой сигнатурой в требуемом возвращаемом типе
// Дальше логика следующая - если setter-метод найден, то с помощью рефлексии получаем значение параметра из входного объекта, и присваиваем это значение возвращаемому объекту "res".
} catch (NoSuchMethodException ex) {
Logger.getLogger(Transparent.class.getName()).log(Level.SEVERE, null, ex);
continue;
} catch (SecurityException ex) {
Logger.getLogger(Transparent.class.getName()).log(Level.SEVERE, null, ex);
continue;
}
}
}
return res;
}
}
private class _init<A> {
private Class<A> type;
public <A> A get(A c) {
A ret = null;
try {
ret = (A) type.newInstance();
} catch (InstantiationException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
}
return ret;
}
}
public class Converter {
public static <A, B> A run(B x, Class<A> type) {
A res = null;
try {
res = type.newInstance();
for (Method m : x.getClass().getMethods()) {
String _methodName = m.getName();
if (m.getDeclaringClass() == x.getClass() && _methodName.startsWith("get")) {
String _fieldName = _methodName.substring(3);
Class[] _paramTypes = new Class[]{m.getReturnType()};
Method _methodName2 = null;
try {
_methodName2 = res.getClass().getMethod("set".concat(_fieldName), _paramTypes);
Object val = m.invoke(x);
if (val.getClass().equals(JAXBElement.class)) {
val = ((JAXBElement<?>) val).getValue();
String _ns = type.getDeclaredField(_fieldName.toLowerCase()).getAnnotation(javax.xml.bind.annotation.XmlElementRef.class).namespace();
QName _qname = new QName(_ns, _fieldName);
val = new JAXBElement(_qname, val.getClass(), type, val);
}
_methodName2.invoke(res, val);
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null);
} catch (NoSuchMethodException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
continue;
} catch (SecurityException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
continue;
} catch (IllegalArgumentException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
continue;
} catch (InvocationTargetException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
continue;
} catch (NoSuchFieldException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
continue;
}
}
}
} catch (InstantiationException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
}
return res;
}
}
Answer the question
In order to leave comments, you need to log in
I wrote to you last time - take Class as an argument to the convert method. Apparently, this is the only way.
In java, "templates" or how to properly say generics, exist only at the time of compilation. There are none in runtime, and when the convert method is called in runtime, absolutely nothing is known about A - so the only possible and correct option is to take Class as the method argument. You can accept a class of type Class - so the typing will be preserved.
I don’t understand anything - in the _init class, the type field is not initialized in any way. There must be a constructor with a class parameter (_init(Class type)), in which the field is set. Then everything should work.
BUT, it is impossible to create a generic object without passing the class (or other information about the class (name, package name, etc.)) - it is impossible. That is, in addition to passing the class to the _init constructor, you also need to pass it to the static method.
PS To write in this style, I think it's disrespectful to the reader of the code (did you read java code conventions?)
PPS Initialized, not initiated
In addition to the previous answer, yes, something like this should work:
private class _init<A> {
private Class<A> type;
public A get(Class<A> type) {
this.type = type;
A ret = null;
try {
ret = type.newInstance();
} catch (InstantiationException ex) {
//...
} catch (IllegalAccessException ex) {
//...
}
return ret;
}
}
A slightly incorrect statement that generics exist only at the time of compilation, more precisely, not in all cases. For example, when inheriting from a class with generics or implementing a generic interface, we have the opportunity to get complete information about the generics themselves at runtime. That is, our converter class will look something like this:
public class Converter<A, B> {
final Class<? extends A> retClass;
protected Converter() {
retClass = findType(getClass());
if (retClass == null) {
throw new RuntimeException("Error while determine generic types");
}
}
public A convert(B from) {
A res = null;
try {
res = retClass.newInstance();
Map<String, Field> bFields = getDeclaredAndInheritedFields(from.getClass(), false);
Map<String, Field> aFields = getDeclaredAndInheritedFields(retClass, false);
for(Field field : bFields.values()) {
if (aFields.containsKey(field.getName())) {
Field aField = aFields.get(field.getName());
if (aField.getType().isAssignableFrom(field.getType())) {
field.setAccessible(true);
aField.setAccessible(true);
aField.set(res, field.get(from));
} else {
// типы не приводятся
}
}
}
} catch (InstantiationException | IllegalAccessException e) {
//Logger.getLogger(Transparent.class.getName()).log(Level.SEVERE, null, ex);
}
return res;
}
@SuppressWarnings("unchecked")
private Class<A> findType(Class<? extends Converter> clazz) {
ParameterizedType genericSuperclass = (ParameterizedType) clazz.getGenericSuperclass();
Type type = genericSuperclass.getActualTypeArguments()[0];
if (type instanceof Class<?>) {
return (Class<A>) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
if (rawType instanceof Class<?>) {
return (Class<A>) rawType;
}
}
return null;
}
public static Map<String, Field> getDeclaredAndInheritedFields(Class<?> type) {
Map<String, Field> allFields = new HashMap<>();
allFields.putAll(getValidFields(type.getDeclaredFields()));
Class parent = type.getSuperclass();
while (parent != null && parent != Object.class) {
allFields.putAll(getValidFields(parent.getDeclaredFields()));
parent = parent.getSuperclass();
}
return allFields;
}
public static Map<String, Field> getValidFields(Field[] fields) {
Map<String, Field> allFields = new HashMap<>();
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {
allFields.put(field.getName(), field);
}
}
return allFields;
}
}
public static class AC {
private String one;
private Date two;
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public Date getTwo() {
return two;
}
public void setTwo(Date two) {
this.two = two;
}
}
public static class BC {
private String one;
private Date two;
public BC(String one, Date two) {
this.one = one;
this.two = two;
}
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public Date getTwo() {
return two;
}
public void setTwo(Date two) {
this.two = two;
}
}
public static void main(String[] args) {
BC bObj = new BC("Test", new Date());
AC obj = new Converter<AC, BC>(){}.convert(bObj);
boolean same = obj.getOne().equals(bObj.getOne()) && obj.getTwo().equals(bObj.getTwo());
if (same) {
System.out.print("Object the same");
}
}
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question