Schema allows a designer to specify optional sequences of child elements as follows;
<?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>
This validates
<?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>
and rejects
<?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>
or
<?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>
Ofcourse if I generate a JAXB class from this I get
...
@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.
which will load all 3 instances without complaining provided I don't set up the Schema validation on the Unmarshaller.
The java instance is then contrary to the Schema, and the following will fail. Even if it is valid I must do extra work to determine what state the RootType instance is in.
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)));
}
}
The question is;
- Is there anyway to represent this more accurately in a Java class hierachy?
Generally Model Group Schema Components specify the ordering of child elements, but Java can only capture the 'bucket of all children' in cases where more complex orderings are specified by nested Model Group Schema components?
The difficulty is we would like to represent these groupings by java classes that do not bind to Element parsing events in a one to one way;
Imagine a @XmlElementSequence annotation existed that allowed a sequence of children to be specified;
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")
}
The marshaller would be relatively simple. The Unmarshaller would have to know to create a Sequencepart when it saw the start of an "A" element tag, and to insert the A values inside that Sequencepart instance.
This is the same question I posted on StackOverflow . This version has a simple Maven project source in case my question needs clarification.