Появилась потребность в создании прозрачного конвертера сложных объектов однотипных по структуре, но различающихся по namespace'у. Изначально имеется два XSD с описанием разных типов. JAXB их отлично обрабатывает и создаёт стабы. Далее по бизнес-логике нужно производить преобразование объекта из одного namespace'а в другой. Когда типов объектов не много, то не сильно утомительно сделать индивидуальный конвертер, но по мере разрастания структуры данных, такой подход уже не эффективен. Поэтому возникла идея написать один универсальный конвертер с прозрачным переносом данных.
Хочется сделать так, чтобы вызов конвертера в программном коде выглядел как нибудь так:
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;
}
}
Всё бы хорошо, но споткнулся на строке "_methodName2 = res.getClass().getMethod(«s...», объект «res» не инициирован, и это логично, ибо «A res = null;».
Варианты «new A()», «A.class», «A.getClass().newInstance()» не рабочие.
Дополнительно пробовал вот такой вариант:
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;
}
}
Инициализацию соответственно провести как «A res = (new _init()).newInstance()», но данный вариант тоже не проходит, так как не допустим в static-контексте.
Собственно вопрос к гуру Java — как можно инициировать объект res в таком статическом методе?
Обновление от 04.09.2013
Проанализировал весь материал, и, похоже никак не получится в runtime'е узнать return-тип, и как следствие накидал следующий код. Код еще пока не окончательный, планируется обработать рекурсивную обработку сложных типов.
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;
}
}