user2714602
user2714602

Reputation: 231

Constraint id attribute to be unique in xsd document

I have this xsd

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/XXX"
    xmlns:tns="http://www.example.org/XXX" elementFormDefault="qualified">

    <simpleType name="Name">
        <restriction base="string"></restriction>
    </simpleType>

    <simpleType name="Phone">
        <restriction base="string">
            <pattern value="\d+"></pattern>
        </restriction>
    </simpleType>

    <simpleType name="Id">
        <restriction base="string">
            <pattern value="[a-zA-Z]\w+"></pattern>
        </restriction>
    </simpleType>

    <complexType name="User">
        <attribute name="Id" type="tns:Id" use="required"></attribute>
        <attribute name="Name" type="tns:Name" use="required"></attribute>
        <attribute name="Phone" type="tns:Phone" use="required"></attribute>
    </complexType>

    <complexType name="ROOT">
        <sequence>
            <element name="User" type="tns:User" minOccurs="0" maxOccurs="unbounded">
                <key name="idUser">
                    <selector xpath="User"></selector>
                    <field xpath="@Id"></field>
                </key>
            </element>
        </sequence>
    </complexType>
    <element name="root" type="tns:ROOT">
    </element>
</schema>

and I'd like to restrict the field Id making it unique inside not the document but just inside a branch of the tree. Obviously in this example nothing changes but the idea is that I could have a sequence of servers inside the root element and for every server the user must be unique but at the same time can exists two users with the same Id, if they don't belong to the same server. For example, this document would be valid if there were just the s1 and s2 but it's not valid because the s3 has two users with the same id inside of it.

<?xml version="1.0" encoding="UTF-8"?>
<tns:XXX xmlns:tns="http://www.example.org/XXX" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/XXX schema.xsd ">  
  <tns:Server Id="s1">
    <tns:User Id="u1" Name="Name" Phone="12" />
    <tns:User Id="u2" Name="Name1" Phone="87654" />
  </tns:Server Id="s2">
  <tns:Server Id="a2">
    <tns:User Id="u2" Name="Name" Phone="12" />
    <tns:User Id="u4" Name="Name1" Phone="87654" />
  </tns:Server>
  <tns:Server Id="s3">
    <tns:User Id="u2" Name="Name" Phone="12" />
    <tns:User Id="u2" Name="Name1" Phone="87654" />
  </tns:Server>
</tns:ACSInfo>

I tried with the key tag but if I try to validate this xml it passes and I'd like it can't!

<?xml version="1.0" encoding="UTF-8"?>
<tns:XXX xmlns:tns="http://www.example.org/XXX" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/XXX schema.xsd ">
  <tns:User Id="a1" Name="Name" Phone="12" />
  <tns:User Id="a1" Name="Name1" Phone="87654" />
</tns:XXX>

Upvotes: 1

Views: 752

Answers (1)

tom redfern
tom redfern

Reputation: 31750

Your schema is not correct to enforce uniqueness. You should use xsd:unique. Also the scope should be <root/> rather than <User/>.

Try it with this:

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

  <xs:simpleType name="Name">
    <xs:restriction base="xs:string" />
  </xs:simpleType>

  <xs:simpleType name="Phone">
    <xs:restriction base="xs:string">
      <xs:pattern value="\d+" />
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="Id">
    <xs:restriction base="xs:string">
      <xs:pattern value="[a-zA-Z]\w+" />
    </xs:restriction>
  </xs:simpleType>

  <xs:complexType name="User">
    <xs:attribute name="Id" type="Id" use="required" />
    <xs:attribute name="Name" type="Name" use="required" />
    <xs:attribute name="Phone" type="Phone" use="required" />
  </xs:complexType>

  <xs:element name="root">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="User" type="User" 
                    minOccurs="0" maxOccurs="unbounded" />
      </xs:sequence>
    </xs:complexType>
    <xs:unique name="idUser">
      <xs:selector xpath="tns:User" />
      <xs:field xpath="@Id" />
    </xs:unique>
  </xs:element>
</xs:schema>

UPDATE

To change the scope of the uniqueness just move it up one level:

 <xs:element name="root">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Server" minOccurs="0" maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="User" type="User" minOccurs="0" maxOccurs="unbounded" />
            </xs:sequence>
          </xs:complexType>
          <xs:unique name="idUser">
            <xs:selector xpath="tns:User" />
            <xs:field xpath="@Id" />
          </xs:unique>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

This will allow you to reuse the user ID but not within a <Server/>

Upvotes: 1

Related Questions