0

I have the following XSD:

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

      <xs:element name="Sea">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="FishSubGroup"
                        minOccurs="0" maxOccurs="unbounded"/>
          </xs:sequence>
          <xs:attribute name="name" type="xs:string" use="optional"/>
        </xs:complexType>
      </xs:element>

      <xs:element name="FishSubGroup" abstract="true"/>

      <xs:element name="Tuna" type="FishType" substitutionGroup="FishSubGroup"/>
      <xs:element name="Carp" type="FishType" substitutionGroup="FishSubGroup"/>
      <xs:element name="Salmon" type="FishType" substitutionGroup="FishSubGroup"/>

      <xs:complexType name="FishType">
        <xs:attribute name="name" type="xs:string"/>
      </xs:complexType>
    </xs:schema>

This XML represents a valid instance:

<?xml version="1.0" encoding="UTF-8"?>
<Sea name="Atlantic Ocean">
  <Tuna name="tuna1"/>
  <Carp name="carp1"/>
  <Carp name="carp2"/>
  <Tuna name="tuna2"/>
  <Salmon name="salmon1"/>
</Sea>

Note: I have taken the nice aquatic example from this post.

Question:

Is there a way in XSD to define a constraint to have only a single substituted element of its category? In other words, I would like to have a maximum of 1 Tuna, 1 Carp, 1 Salmon etc. as shown next:

<?xml version="1.0" encoding="UTF-8"?>
<Sea name="Atlantic Ocean">
  <Tuna name="tuna1"/>
  <Carp name="carp1"/>
  <Carp name="carp2"/> <!-- <-- Invalid Carp Already Defined -->
  <Tuna name="tuna2"/> <!-- <-- Invalid Tuna Already Defined -->
  <Salmon name="salmon1"/>
</Sea>
Franck
  • 1,754
  • 1
  • 13
  • 14

1 Answers1

1

You could possibly do this in XSD 1.0 by ensuring the Carp element has an attribute with fixed value type="Carp", and similarly for other elements, and then define an xs:unique constraint requiring the implicit type attribute to be unique within Sea.

In XSD 1.1, define an assertion on the Sea element with test="count(*) = count(distinct-values(*/local-name()))". (In English, the number of elements must be the same as the number of distinct element names.)

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • worked with the cryptic test="count(*) = count(distinct-values(*/local-name())) ;-) – Franck Sep 04 '19 at 19:24
  • just found out that the tooling I use on the programming side refuses to deal with xsd v 1.1. Could you elaborate on how to define a xs:unique constraint with the 'implicit' type attribute to be unique within sea? thx – Franck Sep 04 '19 at 20:13
  • Like most people, I don't find it much fun implementing lengthy workarounds using old technology to problems that are more easily solved by using the latest version. I know we all have to do that sometimes in our day jobs, but I don't have to do it on StackOverflow. – Michael Kay Sep 04 '19 at 20:26
  • Agree. Thank you. – Franck Sep 04 '19 at 20:43