最新 JAXB 版本 (Java 7) 中的 XmlID/XmlIDREF 和继承
我使用 @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 上,继承模块不起作用!
I am using @XmlID and @XmlIDREF tags to reference one object from another. It was working fine in Java 6 even with inherited classes. The example code I've created looks like this.
Base class used tags:
@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;
}
}
Inherited class:
@XmlRootElement
public class TheModule extends Module {
TheModule() {}
private String feature;
public TheModule(String id, Module other, String feature) {
super(id, other);
this.feature = feature;
}
}
Container for these classes:
@XmlRootElement
public class Script {
Script() {}
public Script(Collection<Module> modules) {
this.modules = modules;
}
@XmlElementWrapper
@XmlElementRef
Collection<Module> modules = new ArrayList<Module>();
}
When running this example code:
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();
}
}
I receive XML, in Java 6:
<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>
Note that reference to the m2 (TheModule instance) is serialized as expected.
But when the same code is running under Java 7 (Jaxb 2.2.4-1) I receive:
<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>
So you can see that on latest JAXB @XmlIDREF for inherited module is not working!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这个答案是错误。不要依赖它,而是阅读
JAXBContext.newInstance(...)
,参见 这个答案以及下面的评论。我认为您将 JAXB 与以下行混淆了。
您告诉它您想要序列化 XML 类型
Script
、Module
和TheModule
。 JAXB 将以一种特殊方式处理后一种类型的对象,因为您已经提供了它的基类:它向它添加了一个区分属性。这样就可以在序列化的 XML 中区分这两种类型。尝试仅提供
Script
和Module
(所有模块的基类)。事实上,您可以完全省略
Module
。 JAXB 将推断您尝试从上下文序列化的Script
对象中的类型。顺便说一句,这种行为与 Java 6 并不完全相关。它与正在使用的 JAXB 实现相关(好吧,好吧,我知道几乎是同一件事)。在我的项目中,我使用 JAXB 2.2.4-1,它也在 Java 6 和 7 中重现了当前的问题。
哦,还有一件事:您可以使用以下命令将格式化的 XML 发送到,而不是创建一个 StringWriter 并将对象编组到其中,然后将其内容发送到 System.out
标准输出
。也许这可以(稍微)简化进一步的测试。
This answer is wrong. Instead of relying on it read the documentation for
JAXBContext.newInstance(...)
, see this answer and the comments below.I think you confuse JAXB with the following line.
You tell it that you want to serialize the XML types
Script
,Module
andTheModule
. JAXB will treat objects of this latter type in a special way, because you've already supplied its base class: it adds a discriminating attribute to it. This way these two types can be differentiated in the serialized XML.Try supplying only
Script
andModule
, the base class for all modules.In fact, you can leave out
Module
entirely. JAXB will infer types in theScript
object you try to serialize from the context.This behaviour by the way isn't exactly related to Java 6. It's related to the JAXB implementation in use (okay-okay, almost the same thing, I know). In my projects I use JAXB 2.2.4-1, which reproduces the issue at hand in Java 6 and 7 too.
Oh, and one more thing: instead of creating a
StringWriter
and marshalling objects into that, then sending its contents toSystem.out
you can use the following to send formatted XML tostdout
.Maybe this can ease (a tiny little bit) further testing.
这似乎是 JAXB 中的一个已知错误。我遇到了同样的问题,并通过不使用 XmlIDRef 而是使用带有自定义 ID 的 postDeserialize 解决了该问题。
这是一个错误报告:
http://java.net/jira/browse/JAXB-870
我无法使用 Kohányi Róbert 的建议。这对你有用吗?
This seems to be a known bug in JAXB. I had the same problem and resolved it by not using XmlIDRef, but rather using postDeserialize with a custom ID.
Here is a bug report:
http://java.net/jira/browse/JAXB-870
I couldn't use Kohányi Róbert's suggestion. Did that work for you?
如果您在 2017 年仍然坚持使用 Java7,那么有一个简单的方法可以绕过这个问题。问题是当使用 XmlREFId 进行序列化时,Jaxb 无法处理子类。一种解决方案是使用适配器并返回基类的新实例,其 id 与正在序列化的实例相同(仅使用 id,因此我们不需要关心其他字段):
然后,只需在需要的地方使用该适配器:
If you are still stuck with Java7 in 2017, then there is an easy way to circumvent this. The problem is that when serialiazing with a XmlREFId, Jaxb can't cope with sub-classes. A solution is to use an adapter and return an new instance of the base class with the same id as the one being serialized (only the id will be used, so we don't need to bother with the other fields):
Then, just use that adapter wherever needed: