Search code examples
xmlxsd

How to constrain XML element content type based on an attribute value using XSD


I have the following xml file:

<doc id="JOURNAL/otai/04.03/product/root">
   <field name="name1">8043501d8766ee1199370022482955e0</field>
   <field name="name2">false</field>
   <field name="name3">2023-10-09T093500Z</field>
</doc>

I need to create an XSD file that checks the validity of the xml file, but also the content type of each field element based on the value of the name attribute.

For example:

  • 8043501d8766ee1199370022482955e0 has a content of type string.
  • false has a content of type boolean.
  • 2023-10-09T093500Z has a content of type date.

The XSD should throw an error if field node content type does not match those types, for example, whatever should throw an error, because "whatever" cannot be cast to boolean, and so on.

How the XSD should looks like to perform those checks when validating the input xml?

Thank you!

I tried many approaches, but none of them worked. I was playing a bit with Type Alternative Schema Component, but I was unable to make it work.

This is what I tried:

<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Liquid Technologies Online Tools 1.0 (https://www.liquid-technologies.com) -->
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="doc">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="field">
          <xs:complexType>
            <xs:simpleContent>
              <xs:extension base="xs:string">
                <xs:attribute name="name" use="required" >
                    <xs:simpleType>
                        <xs:restriction base="xs:string">
                            <xs:enumeration value="name1"/>
                                                        <xs:enumeration value="name2"/>
                                                        <xs:enumeration value="name3"/>
                        </xs:restriction>
                    </xs:simpleType>
                </xs:attribute>
                <xs:attribute name="copyTo" type="xs:string" use="optional" />
              </xs:extension>
            </xs:simpleContent>
          </xs:complexType>
          <xs:alternative test="@name='name2'">
            <xs:element name="field" type="xs:boolean"/>
          </xs:alternative>
        </xs:element>
      </xs:sequence>
      <xs:attribute name="id" type="xs:string" use="required" />
    </xs:complexType>
  </xs:element>
</xs:schema>

If I try to validate the xml, I got the following error:

s4s-elt-must-match.1: The content of 'field' must match (annotation?, (simpleType | complexType)?, (unique | key | keyref)*)). A problem was found starting at: alternative.

I expect the XSD to validate the content of the field element based on the value of the name attribute.

e.g. field element with name2 as name attribute value should only be valid if field element content can be parse to boolean.


Solution

  • Please try the following XSD 1.1 that is using a Conditional Type Assignment (CTA).

    XML

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
               elementFormDefault="qualified">
    
        <xs:element name="doc">
            <xs:complexType>
                <xs:sequence>
                    <xs:element maxOccurs="unbounded" ref="field"/>
                </xs:sequence>
                <xs:attribute name="id" use="required"/>
            </xs:complexType>
        </xs:element>
    
        <xs:element name="field" type="xs:anySimpleType">
            <xs:alternative test="@name eq 'name1'" type="StringType"/>
            <xs:alternative test="@name eq 'name2'" type="BooleanType"/>
            <xs:alternative test="@name eq 'name3'" type="DateTimeType"/>
            <xs:alternative type="xs:error"/>
        </xs:element>
    
        <xs:complexType name="StringType">
            <xs:simpleContent>
                <xs:extension base="xs:string">
                    <xs:attributeGroup ref="GrpAttributes"/>
                </xs:extension>
            </xs:simpleContent>
        </xs:complexType>
    
        <xs:complexType name="BooleanType">
            <xs:simpleContent>
                <xs:extension base="xs:boolean">
                    <xs:attributeGroup ref="GrpAttributes"/>
                </xs:extension>
            </xs:simpleContent>
        </xs:complexType>
    
        <xs:complexType name="DateTimeType">
            <xs:simpleContent>
                <xs:extension base="xs:dateTime">
                    <xs:attributeGroup ref="GrpAttributes"/>
                </xs:extension>
            </xs:simpleContent>
        </xs:complexType>
    
        <xs:attributeGroup name="GrpAttributes">
            <xs:attribute name="name" type="NameAttrType" use="required"/>
            <xs:attribute name="copyTo" type="xs:string"/>
        </xs:attributeGroup>
    
        <xs:simpleType name="NameAttrType">
            <xs:restriction base="xs:string">
                <xs:whiteSpace value="preserve"/>
                <xs:maxLength value="100"/>
                <xs:enumeration value="name1"/>
                <xs:enumeration value="name2"/>
                <xs:enumeration value="name3"/>
            </xs:restriction>
        </xs:simpleType>
    </xs:schema>