Search code examples
xmlxsdsequencerepeat

xsd: multiple choice without repetition


I need a XML like this:

<permission>
     <userType>Root</userType>
     <userType>Admin</userType>
 </permission>

and the values are from an enumeration like this:

<xs:simpleType name="USERS"> 
    <xs:restriction base="xs:string"> 
        <xs:enumeration value="Root"/> 
        <xs:enumeration value="Admin"/> 
        <xs:enumeration value="User"/> 
        <xs:enumeration value="Guest"/> 
    </xs:restriction> 
</xs:simpleType>

the problem is that how I've it right now it accepts repeated values.

<xs:element name="permission">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="userType" minOccurs="0" maxOccurs="unbounded" type="USERS"/>
                </xs:sequence>
            </xs:complexType>
        </xs:element>

how can I do it, so that there's only 1 occurrence of each value available in the enum?


Solution

  • You can add an uniqueness constraint for the contents of userType:

    <xs:element name="permission">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="userType" minOccurs="0" maxOccurs="unbounded" type="USERS"/>
            </xs:sequence>
        </xs:complexType>
        <xs:unique name="uniqueUserType">
            <xs:selector xpath="userType"/>
            <xs:field xpath="."/>
        </xs:unique>
    </xs:element>
    

    With this constraint, a duplicate name in userType will not validate:

    <permission>
        <userType>Root</userType>
        <userType>Admin</userType>
        <userType>Admin</userType>
    </permission>
    

    If your schema declares a target namespace, you will need to declare a prefix for it so that it can be found by the XPath expression (which considers non-prefixed selectors as belonging to no-namespace), even if you have a default namespace declared. For example, you will need to have:

    <xs:schema ... targetNamespace="my-namespace" xmlns:prefix="mynamespace"> ...
    

    and use that prefix in your location paths:

    <xs:selector xpath="prefix:userType"/>