Reputation: 3717
This is a similar question to How to create a schema for an unordered list of XML nodes, with occurrence constraints, but actually slightly simpler. However I am having great trouble understanding the logic behind sequences and choices (and especially when they are nested into sequences of choices or choices of sequences), and although I've studied it for a long time I can't understand how the example above works.
What I need is schema for a list of nodes, ChildA and ChildB, whereby ChildA can occur 0-n times, but ChildB only 0-1 times. (Actually I need several nodes of each type, but if I can do it for ChildA and ChildB, extending it to ChildX etc. and ChildY etc. should be simple). There should be no order constraint. I'd appreciate any help. Any links that explain schema indicators in depth would also be helpful.
Upvotes: 3
Views: 3164
Reputation: 13966
This would be the simplest solution that quickly came to my mind. The key point here is to use another sequence inside the "main" sequence. The schema is kept deterministic by setting the inner sequence to start with <ChildB>
and <ChildB>
is kept optional by setting the cardinality of that sequence to 0-1.
This is an XMLSchema 1.0 solution.
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- Schema for elements ChildA and ChildB
The requirements are as follows:
* ChildA and ChildB may occur in any order.
* ChildA is optional and may occur multiple times.
* ChildB is optional and may occur once only.
-->
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="AB-container" type="AB-type" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="AB-type">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="ChildA" type="xs:string" />
<xs:sequence minOccurs="0">
<xs:element name="ChildB" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="ChildA" type="xs:string" />
</xs:sequence>
</xs:sequence>
</xs:complexType>
</xs:schema>
Upvotes: 3
Reputation: 839
@Dave is adding some dumb attribute to ChildB okay? Since your requirement on childB is 0-1 we can achieve the desired solution by adding a fixed attribute to childB and enforcing unique constraint on the attribute.
<complexType name="childAType">
<simpleContent>
<extension base="string"></extension>
</simpleContent>
</complexType>
<complexType name="childBType">
<simpleContent>
<extension base="string">
<attribute name="value" type="string" fixed="123"></attribute>
</extension>
</simpleContent>
</complexType>
<element name="root">
<complexType>
<choice minOccurs="0" maxOccurs="unbounded">
<element name="childA" type="tns:childAType" minOccurs="0" maxOccurs="unbounded"></element>
<element name="childB" type="tns:childBType" minOccurs="0" maxOccurs="unbounded"></element>
</choice>
</complexType>
<unique name="childB.max.once">
<selector xpath="./tns:childB"></selector>
<field xpath="@value"></field>
</unique>
</element>
Below is one of valid XML (order of B doesn't matter or B can be excluded)
<tns:root xmlns:tns=" ">
<tns:childA></tns:childA>
<tns:childB></tns:childB>
<tns:childA></tns:childA>
<tns:childA></tns:childA>
</tns:root>
However the below one is invalid
<tns:root xmlns:tns=" ">
<tns:childB></tns:childB>
<tns:childA></tns:childA>
<tns:childB></tns:childB>
<tns:childA></tns:childA>
<tns:childA></tns:childA>
</tns:root>
Upvotes: 0
Reputation: 21638
Short answer is that it cannot be done in XSD 1.0; in XSD 1.1 you could use an xsd:all
compositor (since the restriction from XSD 1.0 of having only maxOccurs = 1 has been removed) - however, XSD 1.1 's problems are that i) it is only available freely as a beta Xerces version - as far as I know, and at this time; ii) there's a SAXON edition supporting it, last time I saw references to it you would have to pay for that and iii) you would have a hard time interoperating with other folks since most of them are still on XSD 1.0.
IF you can use Schematron - definitely more accessible than XSD 1.1 since it is just XSLT 1.0/2.0, then it should be easy to code it such that the count of particular element particles meets a specified criteria; it would augment an XSD where the compositor would be a repeating xsd:choice
, where the choice options are elements from your allowed set.
Some people try to explain XSD compositors by making a parallel with constructs from regular expressions. If you are familiar with that, then xsd:all
in XSD 1.0 is similar to square brackets (much simpler though, no concept of a range or negation), xsd:choice
is like | (pipe, alternation) and xsd:sequence
is the rest (where it matters the order in which you write your stuff).
I see that other people on SO recommend W3Schools. I didn't try it myself, hence me passing this on to you with the disclaimer.
Upvotes: 2