为什么JAXB有时会映射到JAXBElement?

18

非官方指南上有一个占位回答,并附有一篇文章链接(对我来说)似乎与此无关。

我使用XJC生成我的JAXB类,虽然大多数类与预期相互映射,但某些元素会被映射到 JAXBElement<Foo>。对于具有循环的图形而言,这最令人恼火,因为有时候 Foo 元素的父节点将是 JAXBElement<Foo>,它本身没有 parent 属性,从而打破了循环。

我可以想出各种解决方法,但如果有人能向我解释这种行为,那就更好了。为什么JAXB有时会将 <Foo> 元素映射到 JAXBElement<Foo> 而不是 Foo?


请见 https://dev59.com/eVDTa4cB1Zd3GeqPHj9j#3639375 以了解该问题的解释。 - skaffman
@skaffman:嗯?我看不出你的回答与xjc是如何选择JAXBElement<Foo>而不是Foo有关。 - C. K. Young
1
@Chris:这部分是由源模式中匿名类型或命名类型的选择决定的。 - skaffman
2个回答

11

JAXBElement被用于在对象模型中没有足够信息的情况下保留元素名称/命名空间。最常见的情况是使用替代组:

使用替代组:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.example.org" 
    xmlns="http://www.example.org" 
    elementFormDefault="qualified">

    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="anElement"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="anElement" type="xs:string"/>

    <xs:element name="aSubstituteElement" type="xs:string" substitutionGroup="anElement"/>

</xs:schema>

将生成:

package org.example;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "anElement"
})
@XmlRootElement(name = "root")
public class Root {

    @XmlElementRef(name = "anElement", namespace = "http://www.example.org", type = JAXBElement.class)
    protected JAXBElement<String> anElement;

    public JAXBElement<String> getAnElement() {
        return anElement;
    }

    public void setAnElement(JAXBElement<String> value) {
        this.anElement = ((JAXBElement<String> ) value);
    }

}

没有替代组:

如果您移除了替代组:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.example.org" 
    xmlns="http://www.example.org" 
    elementFormDefault="qualified">

    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="anElement"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="anElement" type="xs:string"/>

</xs:schema>
以下类将被生成:
package org.example;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "anElement"
})
@XmlRootElement(name = "root")
public class Root {

    @XmlElement(required = true)
    protected String anElement;

    public String getAnElement() {
        return anElement;
    }

    public void setAnElement(String value) {
        this.anElement = value;
    }

}
你解组时可能会得到一个JAXBElement对象,对比以下例子:

谢谢,替换组确实是发生这种情况的地方。那么,在 afterUnmarshal(Unmarshaller u, Object parent) 中,这些可替换类被传递自己的JAXBElement作为它们的“parent”,这是一个错误吗?特别是因为似乎没有办法从JAXBElement中获取真正的父元素。 - TL Stillman
这似乎是Metro JAXB中的一个错误(Java SE 6中包含的参考实现)。在MOXy JAXB实现(http://www.eclipse.org/eclipselink/moxy.php)中,正确的父对象被传递给afterUnmarshal方法。 - bdoughan

0
如何避免映射到JAXBElement? 我已经做了这些事情,它对我有用:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
           xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
           jaxb:extensionBindingPrefixes="xjc"
           jaxb:version="2.0">

    <xs:annotation>
       <xs:appinfo>
          <jaxb:globalBindings generateValueClass="false">
           <xjc:simple />
          </jaxb:globalBindings>
       </xs:appinfo>
    </xs:annotation>

</xs:schema>

将此文件保存在 XML 文件中,并提供绑定文件。

参考 链接

使用 Eclipse 生成 JAXB 类,别忘了使用上述 XML 创建绑定文件并勾选“允许供应商扩展”,如下所示:

enter image description here

通过更改,我能够在将xsd转换为JAXB生成的类时,在生成的xjc类中摆脱JAXBElement。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接