XSD: How to have (A*|B*), C?, (A*|B*)

146 Views Asked by At

I have a XSD with XML elements A, B, and C. I would like these to appear in a parent element in this way:

A and B can appear any times, C only once, optionally. The order does not matter, but A and B should ideally be "together", (so it's rather (A*), C?, (B*) | (B*), C?, (A*)), but the "A and B together" is not necessary).

How can I achieve that with XSD?


Actual XSD what I have tried:

<!-- Hint -->
<xs:element name="hint">
    <xs:complexType mixed="true">
        <xs:sequence>
            <xs:choice>
                <xs:element ref="tag" minOccurs="0" maxOccurs="unbounded"/>
                <xs:element ref="link" minOccurs="0" maxOccurs="unbounded" />
            </xs:choice>
            <xs:sequence>
                <xs:element name="message" minOccurs="0" maxOccurs="1"/>
            </xs:sequence>
            <xs:choice>
                <xs:element ref="tag"  minOccurs="0" maxOccurs="unbounded"/>
                <xs:element ref="link" minOccurs="0" maxOccurs="unbounded"/>
            </xs:choice>
        </xs:sequence>
    </xs:complexType>
</xs:element>

But that gives me

cos-nonambig: <ns>:link and <ns>:link (or elements from their substitution group) violate "Unique Particle Attribution". During validation against this schema, ambiguity would be created for those two particles.

3

There are 3 best solutions below

1
On

The "Unique Particle Attribution" basically means that every element during validation needs to be deterministically assignable to certain XSD "branch". And that can be checked when validating the XSD.

I found out that the (only?) way to avoid abiguity is to list all combinations of A,B,C in a <xs:choice>, while setting minOccurs="1" as appropriate to break the ambiguity. That gives quite a few choices, but for 3 elements, still doable.

This is for the case where I leave 'C' in the beginning:

        <xs:sequence>
            <xs:element name="message" minOccurs="0" maxOccurs="1"/>
            <xs:choice>
                <xs:sequence>
                    <xs:element ref="link" minOccurs="1" maxOccurs="unbounded" />
                    <xs:element ref="tag" minOccurs="0" maxOccurs="unbounded"/>
                </xs:sequence>
                <xs:sequence>
                    <xs:element ref="tag" minOccurs="1" maxOccurs="unbounded"/>
                    <xs:element ref="link" minOccurs="0" maxOccurs="unbounded" />
                </xs:sequence>
                <xs:sequence/>
            </xs:choice>
        </xs:sequence>

Not closing this question, I believe there's a better option even in XSD 1.0.

0
On

A and B can appear any times, C only once, optionally. The order does not matter, but A and B should ideally be "together"

XSD 1.0 Solution

You'll have to impose an ordering to avoid violating Unique Particle Attribution:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:complexType name="SolutionType">
      <xs:sequence>
        <xs:element name="A" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element name="C" minOccurs="0"/>
        <xs:element name="B" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>

</xs:schema>

At least this achieves your preference toward keeping A and B together.

0
On

Basically, the need is to have any mix of A, B, C, where C appears only once.

XSD 1.1 Solution

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
           vc:minVersion="1.1">

    <xs:complexType name="SolutionType">
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="A"/>
        <xs:element name="B"/>
        <xs:element name="C"/>
      </xs:choice>
      <xs:assert test="count(c) &lt;= 1"/>
    </xs:complexType>

</xs:schema>

The above xs:assertion assumes that you mean "C appears at most once" but can easily be adjusted to require C to appear exactly once.