Search code examples
xmlxsdxsd-validationxml-validation

Require either both attributes or none to be present by XML schema


I have an element that could look like this:

<MyElement start="12.5. 2020" end="6.6 2020" info="Hello world!"/>

But also this:

<!-- This element still can contain useful info, but is not time-bound -->
<MyElement info="42" />

So I'd like to set the definition of the node so that either non or both of the attributes must be present. So far, I just used documentation to document the requirement, but it would be better if it was directly in the schema:

<xsd:complexType name="MyElement">
  <xsd:attribute name="start" type="MyDate" use="optional">
    <xs:annotation>
      <xs:documentation>end date must also be present!</xs:documentation>
    </xs:annotation>
  </xsd:attribute>
  <xsd:attribute name="end" type="MyDate" use="optional">
    <xs:annotation>
      <xs:documentation>start date must also be present!</xs:documentation>
    </xs:annotation>
  </xsd:attribute>
  <xsd:attribute name="info" type="xsd:string" use="required" />
</xsd:complexType>

Solution

  • XSD 1.0

    Your constraint cannot be enforced in XSD 1.0 alone.

    XSD 1.1

    Your constraint can be enforced in XSD 1.1 using xsd:assert:

        <xsd:assert test="(@start and @end) or (not(@start) and not(@end)) "/>
    

    Show in context:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" 
      elementFormDefault="qualified"
      vc:minVersion="1.1">
      <xsd:complexType name="MyElement">
        <xsd:attribute name="start" type="xsd:string" use="optional">
          <xsd:annotation>
            <xsd:documentation>end date must also be present!</xsd:documentation>
          </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="end" type="xsd:string" use="optional">
          <xsd:annotation>
            <xsd:documentation>start date must also be present!</xsd:documentation>
          </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="info" type="xsd:string" use="required" />
    
        <xsd:assert test="(@start and @end) or (not(@start) and not(@end)) "/>
    
      </xsd:complexType>
      <xsd:element name="MyElement" type="MyElement"/>
    </xsd:schema>