繁体   English   中英

XmlID / XmlIDREF和最新JAXB版本(Java 7)中的继承

[英]XmlID/XmlIDREF and inheritance in latest JAXB version (Java 7)

我使用@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继承模块不起作用!

这个答案是错的 而不是依赖它阅读JAXBContext.newInstance(...)的文档,请参阅此答案和下面的评论。


我认为你把JAXB与以下行混淆了。

JAXBContext.newInstance(Module.class, Script.class, TheModule.class);

您告诉它您要序列化XML类型ScriptModuleTheModule JAXB将以特殊方式处理后一种类型的对象,因为您已经提供了它的基类:它为它添加了一个区别属性。 这样,这两种类型可以在序列化XML中区分。

尝试仅提供ScriptModule ,这是所有模块的基类。

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

也许这可以轻松(一点点)进一步测试。

这似乎是JAXB中的一个已知错误。 我遇到了同样的问题并通过不使用XmlIDRef来解决它,而是使用带有自定义ID的postDeserialize。

这是一个错误报告:

http://java.net/jira/browse/JAXB-870

我无法使用KohányiRóbert的建议。 这对你有用吗?

如果你在2017年仍然坚持使用Java7,那么有一种简单的方法可以避免这种情况。 问题是当使用XmlREFId进行序列化时,Jaxb无法处理子类。 一个解决方案是使用一个适配器并返回一个基类的新实例,其id与被序列化的id相同(只使用id,所以我们不需要打扰其他字段):

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;

    //...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM