Search code examples
xmlxsdxsd-validationxml-validationxsd-1.1

XSD to assert equality of attributes against nested elements?


I am trying to add an assertion to a XSD (1.1) schema that will check an element's attribute for equality against its children element's value.

I have the following XML:

<?xml version="1.0" encoding="UTF-8"?>

<CarList Maker="Tesla">
  <Car>
    <Year>2010</Year>
    <Color>Blue</Color>
    <Price>40000</Price>
    <Maker>Tesla</Maker>
  </Car>
  <Car>
    <Year>2011</Year>
    <Color>White</Color>
    <Price>20000</Price>
    <Maker>Tesla</Maker>
  </Car>
  <Car>
    <Year>2012</Year>
    <Color>Black</Color>
    <Price>55000</Price>
    <Maker>Tesla</Maker>
  </Car>
</CarList>

I want to make sure that the Maker attribute of the CarList element is the only maker used in the subsequent Car elements. I use XSD 1.1 so I tried to do it with an xs:assert as follows:

<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema  xmlns:xs="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="qualified"
            xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
            vc:minVersion="1.1">

<xs:element name="Year" type="xs:positiveInteger"/>
<xs:element name="Color" type="xs:string"/>
<xs:element name="Price" type="xs:positiveInteger"/>
<xs:element name="Maker" type="MakerType"/>

<xs:simpleType name="MakerType">
  <xs:restriction base="xs:string">
    <xs:enumeration value="Tesla"/>
    <xs:enumeration value="GM"/>
    <xs:enumeration value="Ford"/>
  </xs:restriction>
</xs:simpleType>

<xs:element name="Car">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="Year"/>
      <xs:element ref="Color"/>
      <xs:element ref="Price"/>
      <xs:element ref="Maker"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

<xs:element name="CarList">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="Car" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="Maker" type="MakerType" use="required"/>
    <xs:assert test="Car/Maker eq @Maker"/>
  </xs:complexType>
</xs:element>

</xs:schema>

but when I try to validate it (using python xmlschema library) I get the following error:

'/' operator at line 1, column 4: [err:XPTY0004] atomized operand is a sequence of length greater than one

It seems like there is an error with the XPath format of the test attribute in the xs:assert element, more specifically with the way I try to access the sub element Maker of the Car element.

What am I doing wrong? any idea how can I accomplish this assertion?


Solution

  • The Car/Maker XPath in your xs:assert is selecting multiple elements, which cannot be compared directly against an attribute value, @Maker.

    To fix, change your xs:assert from

    <xs:assert test="Car/Maker eq @Maker"/>
    

    to

    <xs:assert test="every $maker in Car/Maker satisfies $maker eq @Maker"/>