使用@XmlPath和jaxb / MOXy映射复杂类型

时间:2013-07-04 09:19:46

标签: java xml jaxb moxy

我有一个深层的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;
    }

}

的Marshaller / Unmarshaller的

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)路径有关。

我还添加了package-info.java以及我在处理这些对象时使用的marshaller / unmarshaller。由于jaxb.properties不包含任何令人兴奋的东西,我已将其遗漏。

2 个答案:

答案 0 :(得分:4)

当我运行你的例子时,一切正常。由于您的真实模型可能具有get / set方法,因此您需要确保向类中添加@XmlAccessorType(XmlAccessType.FIELD),否则MOXy(或任何其他JAXB impl)也会将相应的属性视为已映射(请参阅:{{3 }})。

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/06/using-jaxbs-xmlaccessortype-to.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命名空间限定的节点在s注释中应具有前缀@XmlPath

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中指定名称空间   将路径映射到复杂对象时,可以在跳过时跳过   将路径映射到一个简单的对象,如String或整数。

这是一个错误。对于简单对象,您应该使用与复杂对象相同的方式命名空间@XmlPath(请参阅更新#1)。今天正确的映射工作,我们将修复以下错误,以便不正确的映射行为正确。您可以使用以下链接跟踪我们在该问题上的进展情况:

答案 1 :(得分:2)

因此,在将路径映射到复杂对象时,似乎需要在@XmlPath中指定名称空间,但在将路径映射到简单对象(例如String)时可以跳过该名称空间或integer

原始,错误,代码

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

更新了工作代码

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

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

相关问题