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

195 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

0
kjhughes 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.

0
kjhughes 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.

1
Ondra Žižka 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.