Search code examples
xmlxsduniqueenumeration

XSD assuring that each enumeration value can only occur once as attribute


Let's say I have a schema to define a list of articles. Each of these articles has a list of long names, one for each known language. The language is an attribute at the level of an individual long name.

I want to assure 2 things:

  1. The language attribute can only have one of several pre-determined values (solved using an enumeration).
  2. Within each list of long names, a given value for the language attribute can only occur once. For each article I can only have one English long name, one German long name, one French long name etc.

Following another post (link) I implemented a unique constraint at the longName-level. It will satisfy #1, but not #2. If I have 'EN' twice as longName attribute within the same longNames element, it will consider the xml valid.

Original sample XML

<article>
        <articleNumber>3019</articleNumber>
        <longNames>
            <longName language="EN">
                <description>A French Omelette with Cheese</description>
            </longName>
            <longName language="FR">
                <description>Un omelette DU(???) fromage</description>
            </longName>
        </longNames>
    </article>

Original XSD schema

    <xs:complexType name="ArticleType">
    <xs:sequence>
        <xs:element name="articleNumber" type="xs:integer" />
        <xs:element type="bl:LongNamesType" name="longNames" />
        <xs:element type="bl:ShortNameType" name="shortName" maxOccurs="3" />
        <xs:element type="bl:AllergensType" name="allergens" maxOccurs="3" />
        <xs:element type="bl:IngredientsType" name="ingredients" maxOccurs="3" />
        <xs:element type="bl:PictureType" name="picture" maxOccurs="3" />
        <xs:element name="barcode" type="xs:integer"/>
    </xs:sequence>
</xs:complexType>

<xs:complexType name="LongNamesType">
    <xs:sequence>
        <xs:element name="longName" maxOccurs="unbounded">
            <xs:complexType>
                <xs:sequence>
                    <!-- <xs:element type="bl:LanguageType" name="language" /> -->
                    <xs:element name="description" type="xs:string" />
                </xs:sequence>
                <xs:attribute name="language" use="required">
                    <xs:simpleType>
                        <xs:restriction base="xs:string">
                            <xs:enumeration value="EN" />
                            <xs:enumeration value="FR" />
                            <xs:enumeration value="DE" />
                        </xs:restriction>
                    </xs:simpleType>
                </xs:attribute>
            </xs:complexType>
            <xs:unique name="uniqueLongNameLanguage">
                <xs:selector xpath="longNames" />
                <xs:field xpath="@language" />
            </xs:unique>              
        </xs:element>
    </xs:sequence>
</xs:complexType>

Could anyone help me figure out what I'm doing wrong?


Edit: thanks to Yitzak Khabinsky and Michael Kay I managed to find below working solution. Admittedly it's a bit of a mixture of styles. Thanks to all who replied!

Improved XSD

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xs:schema
    xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"
        xmlns:bl="urn:inventoryinput"
        targetNamespace="urn:inventoryinput">

<xs:element name="inventory" type="bl:InventoryType" />
    <xs:complexType name="InventoryType">
            <xs:sequence>
                <xs:element type="bl:ArticleType" name="article"  maxOccurs="unbounded" />
            </xs:sequence>
    </xs:complexType>
        
    <xs:complexType name="ArticleType">
        <xs:sequence>
            <xs:element name="articleNumber" type="xs:integer" />
            <xs:element ref="bl:longNames" minOccurs="0" />
            <xs:element name="barcode" type="xs:integer"/>
        </xs:sequence>
    </xs:complexType>
    
    <xs:element name="longNames">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="bl:longName" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
        <xs:unique name="uniqueLongNameLanguage">
            <xs:selector xpath="bl:longName" />
            <xs:field xpath="@language" />
        </xs:unique>              
    </xs:element>
    
    <xs:element name="longName" >
      <xs:complexType>
        <xs:sequence>
            <xs:element name="description" type="xs:string" />
        </xs:sequence>
        <xs:attribute name="language" use="required">
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:enumeration value="NL" />
                    <xs:enumeration value="FR" />
                    <xs:enumeration value="DE" />
                </xs:restriction>
            </xs:simpleType>
        </xs:attribute>
    </xs:complexType>
  </xs:element>

...

Reworked XML example

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<inventory xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:inventoryinput">

        <article>
            <articleNumber>3019</articleNumber>
            <longNames>
                <longName language="NL">
                    <description>DE TROG Ardeens bruin brood Bio 600g</description>
                </longName>
                <longName language="FR">
                    <description>DE TROG pain ardennais complet Bio 600g</description>
                </longName>
            </longNames>

Solution

  • Please try the following solution.

    Input XML

    <article>
        <articleNumber>3019</articleNumber>
        <longNames>
            <longName language="EN">
                <description>A French Omelette with Cheese</description>
            </longName>
            <longName language="FR">
                <description>Un omelette DU(???) fromage</description>
            </longName>
        </longNames>
    </article>
    

    XSD

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
        <xs:element name="article">
            <xs:complexType>
                <xs:sequence>
                    <xs:element ref="articleNumber"/>
                    <xs:element ref="longNames"/>
                </xs:sequence>
            </xs:complexType>
        </xs:element>
        <xs:element name="articleNumber" type="xs:integer"/>
        <xs:element name="longNames">
            <xs:complexType>
                <xs:sequence>
                    <xs:element maxOccurs="unbounded" ref="longName"/>
                </xs:sequence>
            </xs:complexType>
            <xs:unique name="uniqueLongNameLanguage">
                <xs:selector xpath="longName"/>
                <xs:field xpath="@language"/>
            </xs:unique>
        </xs:element>
        <xs:element name="longName">
            <xs:complexType>
                <xs:sequence>
                    <xs:element ref="description"/>
                </xs:sequence>
                <xs:attribute name="language" use="required">
                    <xs:simpleType>
                        <xs:restriction base="xs:string">
                            <xs:enumeration value="EN"/>
                            <xs:enumeration value="FR"/>
                            <xs:enumeration value="DE"/>
                        </xs:restriction>
                    </xs:simpleType>
                </xs:attribute>
            </xs:complexType>
        </xs:element>
        <xs:element name="description" type="xs:string"/>
    </xs:schema>