我正在使用@XmlID和@XmlIDREF标记来引用另一个对象。即使使用继承的类,它在Java 6中也能正常工作。我创建的示例代码如下所示。 基类使用的标签:
@XmlRootElement
@XmlAccessorType(FIELD)
public class Module {
Module() {}
@XmlIDREF
private Module other;
@XmlID
private String id;
public Module(String id, Module other) {
this.id = id;
this.other = other;
}
}
继承类:
@XmlRootElement
public class TheModule extends Module {
TheModule() {}
private String feature;
public TheModule(String id, Module other, String feature) {
super(id, other);
this.feature = feature;
}
}
这些类的容器:
@XmlRootElement
public class Script {
Script() {}
public Script(Collection<Module> modules) {
this.modules = modules;
}
@XmlElementWrapper
@XmlElementRef
Collection<Module> modules = new ArrayList<Module>();
}
运行此示例代码时:
public class JaxbTest {
private Script createScript() {
Module m1 = new Module("Module1", null);
Module m2 = new TheModule("Module2", m1, "featured module");
Module m3 = new Module("Module3", m2);
return new Script(Arrays.asList(m1, m2, m3));
}
private String marshal(Script script) throws Exception {
JAXBContext context = JAXBContext.newInstance(Module.class, Script.class, TheModule.class);
Writer writer = new StringWriter();
context.createMarshaller().marshal(script, writer);
return writer.toString();
}
private void runTest() throws Exception {
Script script = createScript();
System.out.println(marshal(script));
}
public static void main(String[] args) throws Exception {
new JaxbTest().runTest();
}
}
我在Java 6中接收XML:
<script>
<modules>
<module>
<id>Module1</id>
</module>
<theModule>
<other>Module1</other>
<id>Module2</id>
<feature>featured module</feature>
</theModule>
<module>
<other>Module2</other>
<id>Module3</id>
</module>
</modules>
</script>
请注意,对m2(TheModule实例)的引用按预期序列化。 但是当在Java 7(Jaxb 2.2.4-1)下运行相同的代码时,我会收到:
<script>
<modules>
<module>
<id>Module1</id>
</module>
<theModule>
<other>Module1</other>
<id>Module2</id>
<feature>featured module</feature>
</theModule>
<module>
<other xsi:type="theModule" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<other>Module1</other>
<id>Module2</id>
<feature>featured module</feature>
</other>
<id>Module3</id>
</module>
</modules>
</script>
所以你可以看到,在最新的JAXB @XmlIDREF上,继承模块无效!
答案 0 :(得分:2)
这个答案错误。请阅读JAXBContext.newInstance(...)
的文档,而不是依赖它,请参阅this answer以及以下评论。
我认为您将JAXB与以下行混淆。
JAXBContext.newInstance(Module.class, Script.class, TheModule.class);
您告诉它您要序列化XML类型Script
,Module
和TheModule
。 JAXB将以特殊方式处理后一种类型的对象,因为您已经提供了它的基类:它为它添加了一个区别属性。这样就可以在序列化的XML中区分这两种类型。
尝试仅提供Script
和Module
,所有模块的基类。
JAXBContext.newInstance(Module.class, Script.class);
事实上,你可以完全忽略Module
。 JAXB将推断您尝试从上下文序列化的Script
对象中的类型。
顺便说一下,这种行为不是与Java 6完全相关的。它与正在使用的JAXB实现有关(好吧 - 好吧,差不多,我知道)。在我的项目中,我使用JAXB 2.2.4-1,它也在Java 6和7中重现了手头的问题。
哦,还有一件事:不是创建StringWriter
并将编组对象编入其中,然后将其内容发送到System.out
,您可以使用以下内容将格式化的XML发送到stdout
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(script, System.out);
也许这可以轻松(一点点)进一步测试。
答案 1 :(得分:0)
这似乎是JAXB中的已知错误。我遇到了同样的问题并通过不使用XmlIDRef来解决它,而是使用带有自定义ID的postDeserialize。
这是一个错误报告:
http://java.net/jira/browse/JAXB-870
我无法使用KohányiRóbert的建议。这对你有用吗?
答案 2 :(得分:0)
如果你在2017年仍然坚持使用Java7,那么有一种简单的方法可以避免这种情况。问题是当使用XmlREFId进行序列化时,Jaxb无法处理子类。一个解决方案是使用一个适配器并返回一个基类的新实例,其id与被序列化的id相同(只使用id,所以我们不需要打扰其他字段):< / p>
public class ModuleXmlAdapter extends XmlAdapter<Module, Module> {
@Override
public Module unmarshal(Module v) {
// there is no problem with deserializing - return as is
return v;
}
@Override
public Module marshal(Module v) {
if (v == null) {
return null;
}
// here is the trick:
return new Module(v.getId(), null);
}
}
然后,只需在任何需要的地方使用该适配器:
public class Module {
@XmlIDREF
@XmlJavaTypeAdapter(ModuleXmlAdapter.class)
private Module other;
//...
}