JAXB:用 java 对象层次结构表示嵌套模型组架构组件

JAXB: Represent nested Model Group Schema Components with a java object hierarchy

架构允许设计者指定可选的子元素序列,如下所示;

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

    <complexType name="RootType">
        <sequence>
            <choice>
                <sequence minOccurs="1" maxOccurs="1">
                    <element name="A" type="tns:A"></element>
                    <element name="B" type="tns:B"></element>
                    <element name="C" type="tns:C"></element>
                </sequence>
                <element name="P" type="tns:P" minOccurs="1" maxOccurs="1"></element>
                <element name="Q" type="tns:Q" minOccurs="1" maxOccurs="1"></element>
            </choice>
        </sequence>
    </complexType>

    <complexType name="A">
        <sequence>
            <element name="one" type="string"/>
            <element name="two" type="string"/>
            <element name="three" type="string"/>
        </sequence>
    </complexType>

    <complexType name="B">
        <sequence>
            <element name="one" type="string"/>
            <element name="two" type="string"/>
            <element name="three" type="string"/>
        </sequence>
    </complexType>

    <complexType name="C">
        <sequence>
            <element name="one" type="string"/>
            <element name="two" type="string"/>
            <element name="three" type="string"/>
        </sequence>
    </complexType>

    <complexType name="P">
        <sequence>
            <element name="one" type="string"/>
            <element name="two" type="string"/>
            <element name="three" type="string"/>
        </sequence>
    </complexType>

    <complexType name="Q">
        <sequence>
            <element name="one" type="string"/>
            <element name="two" type="string"/>
            <element name="three" type="string"/>
        </sequence>
    </complexType>

    <element name="TopLevel" type="tns:RootType"></element>
</schema>

这验证了

<?xml version="1.0" encoding="UTF-8"?>
<tns:TopLevel xmlns:tns="http://www.example.org/Schema1">
    <tns:A>
        <tns:one>tns:one</tns:one>
        <tns:two>tns:two</tns:two>
        <tns:three>tns:three</tns:three>
    </tns:A>
    <tns:B>
        <tns:one>tns:one</tns:one>
        <tns:two>tns:two</tns:two>
        <tns:three>tns:three</tns:three>
    </tns:B>
    <tns:C>
        <tns:one>tns:one</tns:one>
        <tns:two>tns:two</tns:two>
        <tns:three>tns:three</tns:three>
    </tns:C>
</tns:TopLevel>

并拒绝

<?xml version="1.0" encoding="UTF-8"?>
<tns:TopLevel xmlns:tns="http://www.example.org/Schema1">
    <tns:A>
        <tns:one>tns:one</tns:one>
        <tns:two>tns:two</tns:two>
        <tns:three>tns:three</tns:three>
    </tns:A>
    <tns:B>
        <tns:one>tns:one</tns:one>
        <tns:two>tns:two</tns:two>
        <tns:three>tns:three</tns:three>
    </tns:B>
</tns:TopLevel>

   <?xml version="1.0" encoding="UTF-8"?>
    <tns:TopLevel xmlns:tns="http://www.example.org/Schema1">
        <tns:A>
            <tns:one>tns:one</tns:one>
            <tns:two>tns:two</tns:two>
            <tns:three>tns:three</tns:three>
        </tns:A>
        <tns:B>
            <tns:one>tns:one</tns:one>
            <tns:two>tns:two</tns:two>
            <tns:three>tns:three</tns:three>
        </tns:B>
        <tns:C>
            <tns:one>tns:one</tns:one>
            <tns:two>tns:two</tns:two>
            <tns:three>tns:three</tns:three>
        </tns:C>
        <tns:P>
            <tns:one>tns:one</tns:one>
            <tns:two>tns:two</tns:two>
            <tns:three>tns:three</tns:three>
        </tns:P>
    </tns:TopLevel>

当然,如果我从中生成 JAXB class,我会得到

...
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "RootType", propOrder = {
    "a",
    "b",
    "c",
    "p",
    "q"
})
//Manually added for testing 
@XmlRootElement(name="TopLevel")
public class RootType {

    @XmlElement(name = "A")
    protected A a;
    @XmlElement(name = "B")
    protected B b;
    @XmlElement(name = "C")
    protected C c;
    @XmlElement(name = "P")
    protected P p;
    @XmlElement(name = "Q")
    protected Q q;

    /**
     * Gets the value of the a property.
     * 
     * @return
     *     possible object is
     *     {@link A }
     *     
     */
    public A getA() {
        return a;
    }

    /**
     * Sets the value of the a property.
     * 
     * @param value
     *     allowed object is
     *     {@link A }
     *     
     */
    public void setA(A value) {
        this.a = value;
    }

    ....

    etc.

如果我没有在 Unmarshaller 上设置模式验证,它将加载所有 3 个实例而不会抱怨。

java实例则与Schema相反,下面会失败。即使它有效,我也必须做额外的工作来确定 RootType 实例所处的状态。

private static void validate(uk.co.his.test.complexSequenceAndChoice.generated.nact1moxy.RootType root)
{
    if(root.getA()!=null)
    {
        Assert.assertTrue("RootType should either be a sequence of A, B, C or a sinle P or a single Q", root.getB()!=null&&root.getC()!=null&&root.getP()==null&&root.getQ()==null);
    }
    else
    {
        Assert.assertTrue("RootType should either be a sequence of A, B, C or a sinle P or a single Q", root.getB()==null&&root.getC()==null
                &&((root.getP()!=null&&root.getQ()==null)
                || (root.getP()==null&&root.getQ()!=null)));
    }
}

问题是;

困难在于我们想用 java class 不以一对一方式绑定到元素解析事件的元素来表示这些分组;

假设存在允许指定子序列的@XmlElementSequence 注释;

package uk.co.his.test.complexSequenceAndChoice.manual5;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlType;

@XmlType(name = "RootType")
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class RootType {

    @XmlElements({
        @XmlElementSequence(Sequencepart.class), 
        @XmlElement(name="P", type=P.class), 
        @XmlElement(name="Q", type=Q.class)
        })
    public Object partA;

}


package uk.co.his.test.complexSequenceAndChoice.manual5;

import javax.xml.bind.annotation.XmlElement;

import uk.co.his.test.complexSequenceAndChoice.generated.nact1.A;
import uk.co.his.test.complexSequenceAndChoice.generated.nact1.B;

@XmlElementSequenceDef(propOrder = {
    "a",
    "b",
    "c"
})
public class Sequencepart {

    @XmlElement(name = "A")
    protected A a;
    @XmlElement(name = "B")
    protected B b;
    @XmlElement(name = "C")

}

编组器会相对简单。 Unmarshaller 在看到 "A" 元素标记的开始时必须知道创建一个 Sequencepart,并在该 Sequencepart 实例中插入 A 值。

我已经问过 this on the MOXy site,并包含了一个示例 Maven 项目。

这可以使用 MOXy 特定注释来完成。

有关详细信息,请参阅 "Creating "Self“映射”部分 here

如果我用 MOXy @XmlPath 注释对我的 JAXB class 进行注释,如下所示;

import java.util.Optional;

import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import org.eclipse.persistence.oxm.annotations.XmlPath;



@XmlAccessorType(XmlAccessType.NONE)
@XmlType(name = "RootType", propOrder = {
    "seq1",
    "p",
    "q"
})
@XmlRootElement(name="TopLevel")
public class RootType {


    @XmlPath(".")
    private InnerSequence seq1;
    @XmlElement(name = "P")
    private P p;
    @XmlElement(name = "Q")
    private Q q;

    public static class InnerSequence 
    {
        @XmlElement(name = "A")
        public A a;
        @XmlElement(name = "B")
        public B b;
        @XmlElement(name = "C")
        public C c;

        public boolean isEmpty() {
            return c==null&&b==null&&a==null;
        }
        ...
    }
...

我现在可以创建 XML 来验证模式;

<?xml version="1.0" encoding="UTF-8"?>
<TopLevel xmlns="http://www.example.org/Schema1">
   <A>
      <one>Hello One A</one>
   </A>
   <B>
      <one>Hello One B</one>
   </B>
   <C>
      <one>Hello One C</one>
   </C>
</TopLevel>

请注意,无论文件是否包含任何 A、B 或 C,MOXy 都会在解组期间使用 InnerSequence 的实例填充 seq1。

文件;

<?xml version="1.0" encoding="UTF-8"?>
<TopLevel xmlns="http://www.example.org/Schema1">
   <P>
      <one>Hello One A</one>
   </P>
</TopLevel>

仍然使用 InnerSequence 填充 seq1。

我在 Eclipselink 论坛上发布的 answer 有一个附加的 Maven 项目,它提供了更多(工作细节)