Наследование JAXB, не привязанное к подклассу маршалированного класса
Я использую JAXB для чтения и записи XML. Я хочу использовать базовый класс JAXB для сортировки и унаследованный класс JAXB для unmarshalling. Это позволяет Java-приложению отправителя отправлять XML в другое приложение Java-приемника. Отправитель и получатель будут иметь общую библиотеку JAXB. Я хочу, чтобы ретранслятор отменил XML-код в конкретный JAXB-приемник, который расширяет общий класс JAXB.
Пример:
Это обычный класс JAXB, который используется отправителем.
@XmlRootElement(name="person")
public class Person {
public String name;
public int age;
}
Это специфический для получателя класс JAXB, используемый при разборке XML. Класс приемника имеет логику, специфичную для приложения-получателя.
@XmlRootElement(name="person")
public class ReceiverPerson extends Person {
public doReceiverSpecificStuff() ...
}
Маршаллинг работает так, как ожидалось. Проблема заключается в том, что он unmarshalling, но по-прежнему не привязан к Person
, несмотря на JAXBContext, используя имя пакета подкласса ReceiverPerson
.
JAXBContext jaxbContext = JAXBContext.newInstance(package name of ReceiverPerson);
Я хочу, чтобы unmarshall на ReceiverPerson
. Единственный способ, которым я смог это сделать, - удалить @XmlRootElement
из Person
. К сожалению, это предотвращает марксирование Person
. Как будто JAXB начинается с базового класса и работает до тех пор, пока не найдет первый @XmlRootElement
с соответствующим именем. Я попытался добавить метод createPerson()
, который возвращает ReceiverPerson
в ObjectFactory
, но это не помогает.
Ответы
Ответ 1
Вы используете JAXB 2.0 правильно? (с JDK6)
Существует класс:
javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType>
который можно подклассифицировать и переопределить следующие методы:
public abstract BoundType unmarshal(ValueType v) throws Exception;
public abstract ValueType marshal(BoundType v) throws Exception;
Пример:
public class YourNiceAdapter
extends XmlAdapter<ReceiverPerson,Person>{
@Override public Person unmarshal(ReceiverPerson v){
return v;
}
@Override public ReceiverPerson marshal(Person v){
return new ReceiverPerson(v); // you must provide such c-tor
}
}
Использование выполняется следующим образом:
@Your_favorite_JAXB_Annotations_Go_Here
class SomeClass{
@XmlJavaTypeAdapter(YourNiceAdapter.class)
Person hello; // field to unmarshal
}
Я уверен, что, используя эту концепцию, вы можете самостоятельно управлять процессом маршаллинга /unmarshalling (включая выбор правильного типа [sub | super] для построения).
Ответ 2
Следующий фрагмент - это метод теста Junit 4 с зеленым светом:
@Test
public void testUnmarshallFromParentToChild() throws JAXBException {
Person person = new Person();
int age = 30;
String name = "Foo";
person.name = name;
person.age= age;
// Marshalling
JAXBContext context = JAXBContext.newInstance(person.getClass());
Marshaller marshaller = context.createMarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(person, writer);
String outString = writer.toString();
assertTrue(outString.contains("</person"));
// Unmarshalling
context = JAXBContext.newInstance(Person.class, RecieverPerson.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader reader = new StringReader(outString);
RecieverPerson reciever = (RecieverPerson)unmarshaller.unmarshal(reader);
assertEquals(name, reciever.name);
assertEquals(age, reciever.age);
}
Важной частью является использование метода JAXBContext.newInstance(Class... classesToBeBound)
для unmarshalling context:
context = JAXBContext.newInstance(Person.class, RecieverPerson.class);
При этом вызове JAXB вычислит замыкание ссылки на указанные классы и распознает RecieverPerson
. Тест проходит. И если вы измените порядок параметров, вы получите java.lang.ClassCastException
(чтобы они должны были переданы в этом порядке).
Ответ 3
Объект подкласса дважды, один раз для получателя и один раз для отправителя, и только помещаем XmlRootElement в эти подклассы (оставляя суперкласс, Person
, без XmlRootElement). Обратите внимание, что отправитель и получатель совместно используют одни и те же базовые классы JAXB.
@XmlRootElement(name="person")
public class ReceiverPerson extends Person {
// receiver specific code
}
@XmlRootElement(name="person")
public class SenderPerson extends Person {
// sender specific code (if any)
}
// note: no @XmlRootElement here
public class Person {
// data model + jaxb annotations here
}
[проверено и подтверждено работу с JAXB]. Это обходит проблему, которую вы отмечаете, когда несколько классов в иерархии наследования имеют аннотацию XmlRootElement.
Это, возможно, также более аккуратный и более OO-подход, поскольку он отделяет общую модель данных, поэтому он не является "обходным путем" вообще.
Ответ 4
Создайте пользовательский объект ObjectFactory, чтобы создать экземпляр требуемого класса во время unmarshalling. Пример:
JAXBContext context = JAXBContext.newInstance("com.whatever.mypackage");
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.internal.bind.ObjectFactory", new ReceiverPersonObjectFactory());
return unmarshaller;
public class ReceiverPersonObjectFactory extends ObjectFactory {
public Person createPerson() {
return new ReceiverPerson();
}
}
Ответ 5
Я не уверен, почему вы хотели бы сделать это... для меня это не кажется мне безопасным.
Рассмотрим, что произойдет в ReceiverPerson, имеет дополнительные переменные экземпляра... тогда вы закончите с (я думаю), что эти переменные имеют значение null, 0 или false... и что, если null не разрешен или номер должен быть больше 0?
Я думаю, что то, что вы, вероятно, хотите сделать, читается в Person, а затем конструирует новый ReceiverPerson из этого (возможно, предоставляет конструктор, который принимает Person).
Ответ 6
Так как у вас действительно есть два отдельных приложения, скомпилируйте их с разными версиями класса "Человек" - с приложением-получателем, не имеющим @XmlRootElement(name="person")
на Person
. Мало того, что это некрасиво, но он побеждает техническую поддержку, которую вы хотели использовать из того же определения Person как для отправителя, так и для получателя. Его одна функция искушения заключается в том, что она работает.