0

I would like to validate a xml document with an xsd schema file. The xml document contains information about windows services, I would like to set the Name attribute from Service to a unique value.

Here is a small xml example:

<?xml version="1.0" encoding="utf-8"?>
<Services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://example.de/xml/services">
  <Service Name="ALG" StartMode="Manual" State="Stopped">
    <DisplayName>xyz</DisplayName>
  </Service>
  <Service Name="AllUserInstallAgent" StartMode="Manual" State="Stopped">
    <DisplayName>xyz</DisplayName>
  </Service>
  <Service Name="AllUserInstallAgent" StartMode="Manual" State="Stopped">
    <DisplayName>xyz</DisplayName>
  </Service>
    <Service Name="AllUserInstallAgent" StartMode="Manual" State="Stopped">
    <DisplayName>xyz</DisplayName>
  </Service>
</Services>

I tried following with xpath:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://example.de/xml/services" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://example.de/xml/services">
  <xsd:element name="Services">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element maxOccurs="unbounded" name="Service">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="DisplayName" type="xsd:string" />
            </xsd:sequence>
            <xsd:attribute name="Name" type="xsd:string" use="required" />
            <xsd:attribute name="StartMode" type="xsd:string" use="required" />
            <xsd:attribute name="State" type="xsd:string" use="required" />
          </xsd:complexType>
          <xs:unique name="Unique-Name">
            <xs:selector xpath="Service" />
            <xs:field xpath="@Name" />
          </xs:unique>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xs:schema>

But sadly the xml document is still valid. Note that there are some records with the same Name.

What did I wrong? I found this how to make an attribute unique in xml schema? and XML XSD Schema - Enforce Unique Attribute Values in Schema. What is diffrent here?

Community
  • 1
  • 1
Andre Hofmeister
  • 3,185
  • 11
  • 51
  • 74

1 Answers1

2

It's about scope and namespaces.

If you visualize your structure, and keep in mind that the selector is rooted in the element the constraint is associated with...

enter image description here

you may notice that you're looking for a Service under Service... which is not there. So, the first step is to move that under the appropriate element (Services).

enter image description here

The reason why the above still doesn't work has to do with the fact that you're using namespaces, and the elements are qualified. Which means you have to add an XML namespace prefix for your target namespace (tns here). So this is your corrected XSD:

<?xml version="1.0" encoding="utf-8" ?>
<!-- XML Schema generated by QTAssistant/XSD Module (http://www.paschidev.com) -->
<xs:schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://example.de/xml/services" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://example.de/xml/services" xmlns:tns="http://example.de/xml/services">
    <xsd:element name="Services">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element maxOccurs="unbounded" name="Service">
                    <xsd:complexType>
                        <xsd:sequence>
                            <xsd:element name="DisplayName" type="xsd:string"/>
                        </xsd:sequence>
                        <xsd:attribute name="Name" type="xsd:string" use="required"/>
                        <xsd:attribute name="StartMode" type="xsd:string" use="required"/>
                        <xsd:attribute name="State" type="xsd:string" use="required"/>
                    </xsd:complexType>
                </xsd:element>
            </xsd:sequence>
        </xsd:complexType>
        <xs:unique name="Unique-Name">
            <xs:selector xpath="tns:Service"/>
            <xs:field xpath="@Name"/>
        </xs:unique>
    </xsd:element>
</xs:schema>

Which will flag your XML appropriately:

Error occurred while loading [], line 11 position 5
There is a duplicate key sequence 'AllUserInstallAgent' for the 'http://example.de/xml/services:Unique-Name' key or unique identity constraint.
Error occurred while loading [], line 14 position 5
There is a duplicate key sequence 'AllUserInstallAgent' for the 'http://example.de/xml/services:Unique-Name' key or unique identity constraint.
Petru Gardea
  • 21,373
  • 2
  • 50
  • 62
  • Thanks, I will test it instant tomorrow! Which Tool you used? I saw that there is xmlns, xmlns:tns and targetNamespace with the same value, is that necessary? I also tried it without namespace, I removed targetNamespace, but doesn’t work too, why? Have a nive day. – Andre Hofmeister May 14 '13 at 19:32
  • @hofmeister, targetNamespace has to match your requirements; you don't need to define the default namespace with the same value; however, many people believe is a best practice; tns is arbitrary, still you need to define an alias for your targetNamespace and use it since the elements are qualified and if not prefixed, the XPath in selector/field assumes no namespace, NOT what the default namespace is; the tool is [QTAssistant](http://www.paschidev.com) which is what we develop. – Petru Gardea May 15 '13 at 02:35
  • But why it sill doesn’t work if I move the constrain under Services and set the default namespace xmlns? I doesn’t understand why I need xmls, xmls:prefix and targetnamespace.. It would be nice if you would explain it! – Andre Hofmeister May 15 '13 at 06:46
  • @hofmeister, as I said already, it is because the default namespace doesn't apply to the XPath used for selectors and fields; therefore, if you need to identify an element/attribute from your namespace, you then need to define an alias for that. If this "asymmetry" in the spec bothers you (i.e. why does it take a prefix but not the default), then consider the scenario where mixed (qualified/unqualified names) are used in the same XPath. Having the default xmlns eq targetNamespace is just best practice; you don't need it but it is certainly easier since otherwise you would also have to ... – Petru Gardea May 15 '13 at 11:07
  • (cont'd) qualify all other referenced names in your XSD. for e.g. instead of you would need , etc. If you want to get to the bottom of this, then you would have to read on how XSD uses NCName and QName, and how XML namespaces work in general. – Petru Gardea May 15 '13 at 11:10