Java JAXB继承,解组到封送类的子类
我正在使用JAXB读写XML。我想要的是使用一个基本JAXB类进行编组,使用一个继承的JAXB类进行解编组。这是为了允许发送方Java应用程序将XML发送到另一个接收方Java应用程序。发送方和接收方将共享一个公共JAXB库。我希望接收方将XML解组为特定于接收方的JAXB类,该类扩展了通用JAXB类 例如:Java JAXB继承,解组到封送类的子类,java,xml,jaxb,Java,Xml,Jaxb,我正在使用JAXB读写XML。我想要的是使用一个基本JAXB类进行编组,使用一个继承的JAXB类进行解编组。这是为了允许发送方Java应用程序将XML发送到另一个接收方Java应用程序。发送方和接收方将共享一个公共JAXB库。我希望接收方将XML解组为特定于接收方的JAXB类,该类扩展了通用JAXB类 例如: public class YourNiceAdapter extends XmlAdapter<ReceiverPerson,Person>{ @Ov
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
}
}
这是发送方使用的公共JAXB类
@XmlRootElement(name="person")
public class Person {
public String name;
public int age;
}
这是在解组XML时使用的特定于接收方的JAXB类。receiver类具有特定于receiver应用程序的逻辑
@XmlRootElement(name="person")
public class ReceiverPerson extends Person {
public doReceiverSpecificStuff() ...
}
编组工作如期进行。问题在于解组,它仍然解组到Person
,尽管JAXBContext使用子类ReceiverPerson
的包名
JAXBContext jaxbContext = JAXBContext.newInstance(package name of ReceiverPerson);
我要做的是向
ReceiverPerson
解封。我能够做到这一点的唯一方法是从Person
中删除@XmlRootElement
。不幸的是,这样做会阻止Person
被封送。就好像JAXB从基类开始,一直向下,直到找到第一个具有适当名称的@XmlRootElement
。我已尝试添加一个createPerson()
方法,该方法将ReceiverPerson
返回到ObjectFactory
,但没有帮助。我不确定您为什么要这样做。。。对我来说似乎不太安全
考虑一下在ReceiverPerson中会发生什么,因为它有额外的实例变量。。。然后你会发现(我猜)这些变量是null、0或false。。。如果不允许null,或者数字必须大于0,该怎么办
我认为您可能想做的是读入Person,然后从中构造一个新的ReceiverPerson(可能提供一个接受Person的构造函数)。您使用的是JAXB 2.0,对吗?(自JDK6起) 有一类:
javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType>
例如:
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
}
}
我很确定,通过使用这个概念,您可以自己控制编组/解编过程(包括选择要构造的正确[sub | super]类型)。由于您确实有两个独立的应用程序,请使用不同版本的类“Person”编译它们,而接收方应用程序没有
@XmlRootElement(name=“Person”)
在个人
上。这不仅是丑陋的,而且它还破坏了您希望在发送方和接收方使用相同的Person定义时的可维护性。它的一个可取之处在于它可以正常工作。以下代码段是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);
}
通过此调用,JAXB将对指定的类计算引用闭包,并识别
receiverperson
。考试通过了。如果您更改参数顺序,您将得到一个java.lang.ClassCastException
(因此它们必须按此顺序传递)。将Person子类化两次,一次用于receiver,一次用于sender,并且只将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注释时,它避免了您注意到的问题
这也可以说是一种更简洁、更面向对象的方法,因为它将公共数据模型分离出来,因此根本不是一种“变通方法”。创建一个自定义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();
}
}
接管人没有其他变量。它的目的是做特定于接收者的东西。在某种程度上,你仍然需要将它创建为一个不同的类。。。通常的方法是通过构造函数来实现,它接受你想要转换的类型。而且,现在的情况就是这样并不意味着你将来就不会添加vars了。哎呀,这是关于类将来会做什么的很多猜测。如果它在今天已经足够好了,那么它就足够好了,而且你可以在以后重构…公共API的消费者喜欢它们的变化。。。不重构对于内部事物来说是好的,但是任何公开的事物都不是。重构的全部意义在于在不破坏事物的情况下改变事物在幕后的工作方式。如果您正在更改公共API,那么您不是在重构,您是在编写。我尝试了这个,但它不起作用。无论我尝试什么,ObjectFactory或我的XmlAdapter都不会被调用。从我所读到的Sun的JAXB通过静态类引用解析。Glassfish的实现似乎有更多的承诺,XmlJavaAdapters适合于使非JAXB注释类可用于JAXB。因为Person和ReceiverPerson(和/或他们的超类,如果有的话)有JAXB注释,所以它不起作用。您需要不带JAXB的Person和ReceiverPerson,以及两者的适配器。您是否尝试过将类型交换为marshall | unmarshall?[…]扩展XmlAdapter[…]覆盖公共接收人解组(Person v){返回新的接收人(v);}@覆盖公共接收人封送(接收人v){返回v;}请忽略第一条注释;)它还必须在JAXB注释类型上工作,为什么不呢?(我在说明书里找不到什么东西)。只需将“extends XmlAdapter”替换为“extends XmlAdapter”和MarshallUnmarshall,如第二条评论中所述,因为Stackoverflow会自动将投票数最多的回答作为问题的答案。我希望两个应用程序共享公共的基本JAXB类。@Steve:但我的第二个(“整洁方式”)解决方案确实共享公共的基本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
}
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();
}
}