JAXBinheritance,解组编组的子类

我正在使用JAXB来读取和写入XML。 我想要的是使用基本的JAXB类进行编组,并使用inheritance的JAXB类进行取消编组。 这是为了允许发送者Java应用程序发送XML到另一个接收者Java应用程序。 发送者和接收者将共享一个公共的JAXB库。 我希望接收者将XML解组到扩展了genericsJAXB类的接收者特定的JAXB类中。

例:

这是发件人使用的常见JAXB类。

@XmlRootElement(name="person") public class Person { public String name; public int age; } 

这是解组XML时使用的接收器特定的JAXB类。 接收器类具有特定于接收器应用程序的逻辑。

 @XmlRootElement(name="person") public class ReceiverPerson extends Person { public doReceiverSpecificStuff() ... } 

编组按预期工作。 问题在于解组,它仍然对Person进行解组,尽pipeJAXBContext使用了子类ReceiverPerson的包名。

 JAXBContext jaxbContext = JAXBContext.newInstance(package name of ReceiverPerson); 

我想要的是解除ReceiverPerson 。 我已经能够做到这一点的唯一方法是从Person删除@XmlRootElement 。 不幸的是,这样做会阻止Person被封送。 就好像JAXB从基类开始,一直向下找,直到find第一个具有合适名称的@XmlRootElement 。 我试着添加一个createPerson()方法,它返回ReceiverPersonObjectFactory但是没有帮助。

你正在使用JAXB 2.0吗? (自JDK6以来)

有一堂课:

 javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType> 

哪一个可以inheritance,并覆盖以下方法:

 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 } 

我很确定,通过使用这个概念,你可以自己控制编组/解组过程(包括select正确的[sub | super]types来构造)。

下面的代码片段是一个使用绿灯的Junit 4testing的方法:

 @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)方法:

  context = JAXBContext.newInstance(Person.class, RecieverPerson.class); 

通过这个调用,JAXB将在指定的类上计算引用闭包,并将识别RecieverPerson 。 testing通过。 如果你改变参数顺序,你会得到一个java.lang.ClassCastException (所以他们必须按这个顺序传递)。

Subclass Person两次,一次是接收者,一次是发送者,只把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 } 

[testing并确认与JAXB一起工作]。 它避免了您注意到的问题,当inheritance层次结构中的多个类具有XmlRootElement注释时。

这也可以说是一个更整洁,更OO的方法,因为它将通用的数据模型分开了,所以它不是一个“解决方法”。

创build一个自定义的ObjectFactory在解组期间实例化所需的类。 例:

 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(); } } 

我不知道你为什么要这么做…对我来说这似乎并不安全。

考虑ReceiverPerson会有什么额外的实例variables会发生什么…然后你会结束(我猜)这些variables为空,0或false …如果null是不允许的或者数字必须大于0 ?

我认为你可能想要做的是在Person中读取,然后从中构build一个新的ReceiverPerson(可能提供一个带Person的构造函数)。

由于你真的有两个独立的应用程序,编译他们与不同版本的类“Person” – 接收器应用程序不具有@XmlRootElement(name="person") 。 这不仅是丑陋的,而且还会使发送者和接收者使用与Person相同的定义所要求的可维护性。 它的一个补救function是它的工作。