I wrote the following (simplified) schema to validate some XML files I receive:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="Param">
<xs:complexType>
<xs:sequence>
<xs:element name="RadioAddr" type="xs:string" />
<xs:element name="DataToRead" type="xs:integer" minOccurs="0" maxOccurs="1" />
<xs:choice minOccurs="0" maxOccurs="1">
<xs:group ref="Group1" />
<xs:group ref="Group2" />
<xs:group ref="Group3" />
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:group name="Group1">
<xs:sequence>
<xs:element ref="Password" />
<xs:element name="RadioActivated" type="xs:integer" minOccurs="0" maxOccurs="1" />
<xs:element ref="IdNumber" minOccurs="0" maxOccurs="1" />
<xs:element ref="AdjustClock" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:group>
<xs:group name="Group2">
<xs:sequence>
<xs:element ref="IdNumber" minOccurs="0" maxOccurs="1" />
<xs:element ref="Password" />
<xs:element ref="AdjustClock" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:group>
<xs:group name="Group3">
<xs:sequence>
<xs:element ref="IdNumber" minOccurs="0" maxOccurs="1" />
<!-- No password here -->
<xs:element ref="AdjustClock" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:group>
<xs:element name="Password" type="xs:string" />
<xs:element name="IdNumber" type="xs:integer" />
<xs:element name="AdjustClock" type="xs:integer" />
</xs:schema>
When validating this schema, I obtain the following error message:
Not valid. Error - Line 5, 25: org.xml.sax.SAXParseException; lineNumber: 5; columnNumber: 25; cos-nonambig: Password and Password (or elements from their substitution group) violate "Unique Particle Attribution". During validation against this schema, ambiguity would be created for those two particles.
I fully understand the ambiguity but I can't find a solution to make my schema valid.
A possible solution would be to do something like
<xs:element name="Param>
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<!-- put all the possible elements here -->
</xs:choice>
</xs:complexType>
</xs:element>
but my problem with this solution is that I lose one level of abstraction (the groups) that is useful for me further (I use this schema to generate Java classes with JAXB).
So, is there a way to make my schema valid using <xs:group>
or do I have to flatten my schema (like the solution I mentioned above) ?
Here are examples that should be allowed by the XSD:
The minimal allowed:
<Param>
<RadioAddr>1</RadioAddr>
</Param>
Is also legal:
<Param>
<RadioAddr>1</RadioAddr>
<Password>1234</Password>
</Param>
<Param>
<RadioAddr>1</RadioAddr>
<Password>1234</Password>
<RadioActivated>1</RadioActivated>
<IdNumber>12345678</IdNumber>
</Param>
<Param>
<RadioAddr>1</RadioAddr>
<IdNumber>12345678</IdNumber>
<Password>1234</Password>
</Param>
To beat the Unique Particle Attribution violation, you have to have to allow a parser to unambiguously know where it stands in the grammar without having to look ahead more than one element.
The current error arises because it is not possible to know when encountering the Password
element whether the parser is in Group1
or Group2
because IdNumber
is optional. You might make IdNumber
mandatory instead, but that would create ambiguity between Group2
and Group3
over IdNumber
. You might then try using ordering to differentiate the xs:choice
groups, but then you'd find that the optionality of the elements was defeating your effort. You might remove the optionality, and if different ordering among the groups is acceptable, then you may have your answer.
However, that would be a rather odd grammar. At that point, you'd probably be better flattening as you mentioned, but rather than using an unbounded xs:choice
, which would allow arbitrary and unbounded repeats of its elements, you can retain some of the occurrence constraints via a simple xs:sequence
of the elements instead:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="Param">
<xs:complexType>
<xs:sequence>
<xs:element name="RadioAddr" type="xs:string" />
<xs:element name="DataToRead" type="xs:integer" minOccurs="0" maxOccurs="1" />
<xs:element name="RadioActivated" type="xs:integer" minOccurs="0" maxOccurs="1" />
<xs:element ref="IdNumber" minOccurs="0" maxOccurs="1" />
<xs:element ref="Password" minOccurs="0"/>
<xs:element ref="AdjustClock" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Password" type="xs:string" />
<xs:element name="IdNumber" type="xs:integer" />
<xs:element name="AdjustClock" type="xs:integer" />
</xs:schema>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With