【问题标题】:Using @XmlPath with jaxb/MOXy to map complex type使用 @XmlPath 和 jaxb/MOXy 映射复杂类型
【发布时间】:2013-07-04 09:19:46
【问题描述】:

我有一个深度 XML 结构,其中包含许多无意义的包装器,我将它们映射到单个 Java 类。用@XmlPath 映射简单的数据类型是在公园里散步,但是当涉及到实际上需要它们自己的类的类型时,我不太确定如何去做,尤其是当这些类型也应该放在一个列表中时。

我在将以下示例中的所有 element 类型映射到我的 Element 类时遇到问题。由于elements 包装器驻留在使用@XmlPath 映射的资源中,我不能使用@XmlElementWrapper,否则我通常会这样做。

示例 XML 结构

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<s:root xsi:schemaLocation="http://www.example.eu/test ResourceSchema.xsd" xmlns:s="http://www.example.eu/test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <s:resource>
        <s:information>
            <s:date>2013-07-04</s:date>
            <s:name>This example does not work</s:name>
        </s:information>
        <s:elements>
            <s:refobj>
                <s:id>1</s:id>
                <s:source>First Source</s:source>
            </s:refobj>
            <s:refobj>
                <s:id>2</s:id>
                <s:source>Second Source</s:source>
            </s:refobj>
            <s:refobj>
                <s:id>5</s:id>
                <s:source>Fifth Source</s:source>
            </s:refobj>
        </s:elements>
    </s:resource>
</s:root>

Root.java

@XmlRootElement(name = "root")
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    @XmlPath("resource/information/date/text()")
    private String date;

    @XmlPath("s:resource/s:information/s:name/text()")
    private String name;

    @XmlPath("resource/elements/refobj")
    private List<RefObj> refObjs;

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

RefObj.java

@XmlRootElement(name = "refobj")
@XmlAccessorType(XmlAccessType.FIELD)
public class RefObj {

    @XmlElement(name = "id")
    private int id;

    @XmlElement(name = "source")
    private String source;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

}

编组器/解组器

public static void main(String[] args) {
    String xml = getXML();

    Root root = null;
    try {
        JAXBContext context = JAXBContext.newInstance(Root.class);

        Unmarshaller unmarshaller = context.createUnmarshaller();

        StringReader stringReader = new StringReader(xml);

        root = (Root) unmarshaller.unmarshal(stringReader);
    } catch (Exception ex) {
        System.err.println("Failed to unmarshal XML!");
    }

    try {
        JAXBContext context = JAXBContext.newInstance(Root.class);
        Marshaller marshaller = context.createMarshaller();

        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.example.eu/test ResourceSchema.xsd");

        StringWriter stringWriter = new StringWriter();
        marshaller.marshal(root, stringWriter);

        System.out.println(new String(stringWriter.toString().getBytes(Charset.forName("UTF-8"))));
    } catch (Exception ex) {
        System.err.println("Failed to marshal object!");
    }

}

package-info.java

@XmlSchema(
        namespace = "http://www.example.eu/test",
        attributeFormDefault = XmlNsForm.QUALIFIED,
        elementFormDefault = XmlNsForm.QUALIFIED,
        xmlns = {
    @XmlNs(
            prefix = "s",
            namespaceURI = "http://www.example.eu/test")
},
        location = "http://www.example.eu/test ResourceSchema.xsd")
package se.example.mavenproject1;

import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

我可以执行应用程序,但是当我解组/编组 XML 内容时没有映射任何元素,而是得到一个仅包含信息的 XML 表示。

更新

在发布上一个示例后,我意识到它实际上按预期工作,这让我更加困惑。尽管我尝试在我的生产代码中复制(以前的)工作示例但没有任何成功,尽管我已经设法将我遇到的问题实际引入示例代码中。由于我需要为出现问题添加命名空间,因此我假设它与命名约定和 X(ml)Path 有关。

我还添加了package-info.java 和我在处理这些对象时使用的编组器/解组器。由于 jaxb.properties 不包含任何令人兴奋的内容,因此我将其省略了。

【问题讨论】:

    标签: java xml jaxb moxy


    【解决方案1】:

    当我运行您的示例时,一切正常。由于您的真实模型可能具有 get/set 方法,因此您需要确保将 @XmlAccessorType(XmlAccessType.FIELD) 添加到您的类中,否则 MOXy(或任何其他 JAXB impl)也会将相应的属性视为已映射(请参阅:http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html)。

    import java.util.List;
    import javax.xml.bind.annotation.*;
    import org.eclipse.persistence.oxm.annotations.XmlPath;
    
    @XmlRootElement(name = "root")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Root {
    
        @XmlPath("resource/information/date/text()")
        private String date;
    
        @XmlPath("resource/information/name/text()")
        private String name;
    
        @XmlPath("resource/elements/element")
        private List<Element> elements;
    
    }
    

    您还需要确保在与域模型相同的包中具有 jaxb.properties 文件,并使用以下条目将 MOXy 指定为您的 JAXB 提供程序(请参阅:http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html)。

    javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
    

    更多信息


    更新 #1

    当您的文档是命名空间限定时,@XmlPath 注释需要考虑这一点。路径中的节点可以根据@XmlSchema 注释限定。

    包裹信息

    在您的 package-info 类中,前缀 s 分配给命名空间 URI http://www.example.eu/test

    @XmlSchema(
            namespace = "http://www.example.eu/test",
            attributeFormDefault = XmlNsForm.QUALIFIED,
            elementFormDefault = XmlNsForm.QUALIFIED,
            xmlns = {
        @XmlNs(
                prefix = "s",
                namespaceURI = "http://www.example.eu/test")
    },
            location = "http://www.example.eu/test ResourceSchema.xsd")
    package se.example.mavenproject1;
    
    import javax.xml.bind.annotation.XmlNs;
    import javax.xml.bind.annotation.XmlNsForm;
    import javax.xml.bind.annotation.XmlSchema;
    

    这意味着使用http://www.example.eu/test 命名空间限定的节点应在@XmlPath 注释中具有前缀s

    import java.util.List;
    import javax.xml.bind.annotation.*;
    import org.eclipse.persistence.oxm.annotations.XmlPath;
    
    @XmlRootElement(name = "root")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Root {
    
        @XmlPath("s:resource/s:information/s:date/text()")
        private String date;
    
        @XmlPath("s:resource/s:information/s:name/text()")
        private String name;
    
        @XmlPath("s:resource/s:elements/s:element")
        private List<Element> elements;
    
    }
    

    更新 #2

    所以似乎需要在@XmlPath 中指定命名空间 将路径映射到复杂对象时,但可以跳过 将路径映射到简单对象,例如字符串或整数。

    这是一个错误。您应该像对待复杂对象一样使用命名空间限定 @XmlPath 的简单对象(参见更新 #1)。正确的映射今天有效,我们将修复以下错误,以便不正确的映射正确运行。您可以使用下面的链接来跟踪我们在该问题上的进展:

    【讨论】:

    • 感谢您的回复,当我实际为我编写的示例代码创建一个项目时,它也适用于我。现在我已经设法通过向 xml 添加命名空间来重新创建我遇到的问题,我还对示例中的类的命名进行了一些更改,以更好地类似于实际(巨大)xml 中的命名,以防万一图书馆内发生了任何神奇的多元化。
    • @JakobPogulis - 我已经更新了地址命名空间限定的答案。
    【解决方案2】:

    因此,在将路径映射到复杂对象时,似乎需要在@XmlPath 中指定命名空间,但在将路径映射到String 或@987654324 等简单对象时可以跳过@。

    原始的,错误的,代码

    @XmlPath("resource/elements/refobj")
    private List<RefObj> refObjs;
    

    更新的工作代码

    @XmlPath("s:resource/s:elements/s:refobj")
    private List<RefObj> refObjs;
    

    将命名空间前缀添加到@XmlPath 时,所有对象都按预期映射。如果有人能证实这一点,并可能解释原因,我很乐意知道这种行为的原因。

    【讨论】:

    • 您应该命名空间限定@XmlPath 用于简单和复杂的映射。当前有一个错误导致您的简单案例正常工作。我已经用更多细节更新了我的答案:stackoverflow.com/a/17466836/383861
    • XMLDirectMapping/XMLCompositeDirectCollectionMapping 的 XPath 无效。 XPath 必须包含属性的@ 符号或以/text() 结尾的文本节点。例如:“@name”或“name/text()”。我用过 ..................@XmlPath("subject/title") private List title;
    猜你喜欢
    • 2012-08-09
    • 2011-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-23
    • 1970-01-01
    相关资源
    最近更新 更多