3

I have a deep XML structure with a lot of pointless wrappers I'm mapping to a single Java class. Mapping the simple datatypes with @XmlPath is a walk in the park, but when it comes to types that actually require their own class I'm not quite sure how to do it, especially when those types should be put in a list as well.

I'm having problems to map all the element types in the below example to my Element class. Since the elements wrapper resides in resource which is mapped using @XmlPath I can not use the @XmlElementWrapper, which would otherwise be the way I usually would do this.

Example XML structure

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

I can execute the application, but no element is mapped when I unmarshal/marshal the XML content, instead I get a XML representation containing just the information.

Update

After posting the previous example I realized that it actually worked as intended, which made me even more confused. Although I've tried to replicate the (previously) working example in my production code without any success, although I've managed to actually introduce the problems I'm having into the example code. Since I needed to add a namespace for the problems to appear I'm assuming it has something to do with naming conventions and X(ml)Path.

I've also added package-info.java and the marshaller/unmarshaller I'm using when working with those objects. Since the jaxb.properties doesn't contain anything exciting I've left it out.

4

2 回答 2

5

当我运行您的示例时,一切正常。由于您的真实模型可能具有 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具有前缀。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 中指定命名空间,但在将路径映射到简单对象(如字符串或整数)时可以跳过。

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

于 2013-07-04T09:35:16.323 回答
2

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

原始,错误,代码

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

更新的工作代码

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

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

于 2013-07-04T12:23:15.217 回答