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 层次结构中更准确地表示这一点?
一般Model Group Schema Components指定子元素的顺序,但是Java只能在嵌套Model Group Schema组件指定更复杂顺序的情况下捕获'bucket of all children'?
困难在于我们想用 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 项目,它提供了更多(工作细节)
架构允许设计者指定可选的子元素序列,如下所示;
<?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 层次结构中更准确地表示这一点? 一般Model Group Schema Components指定子元素的顺序,但是Java只能在嵌套Model Group Schema组件指定更复杂顺序的情况下捕获'bucket of all children'?
困难在于我们想用 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 项目,它提供了更多(工作细节)