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()
方法,它返回ReceiverPerson
到ObjectFactory
但是没有帮助。
你正在使用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是它的工作。