Home > Software engineering >  Conditionally make the child element value length validation based on the parent attribute name in X
Conditionally make the child element value length validation based on the parent attribute name in X

Time:01-03

I need to validate the following XML:

<nodes>
   <node>
      <tag name="id">
         <value>345</value>
      </tag>
      <tag name="client">
         <value>test</value>
      </tag>
      <tag name="section">
         <value>test2</value>
      </tag>
      <tag name="token">
         <value>79467</value>
      </tag>
   </node>
</nodes>

Here, the attributes names are fixed. Based on the the names, value can be empty for certain fields while it's required for certain fields. Ex, if the attribute name is token, the child element can have empty value. The XSD currently I have is (based on XSD 1.0):

<xs:schema elementFormDefault="qualified"
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="nodes">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="node" maxOccurs="unbounded" minOccurs="0"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="node">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="tag" maxOccurs="unbounded" type="tagType" minOccurs="0"/>
            </xs:sequence>
        </xs:complexType>
        <xs:unique name="TagUniquenessCheck">
            <xs:selector xpath="tag"/>
            <xs:field xpath="@name"/>
        </xs:unique>
    </xs:element>
    <xs:complexType name="tagType">
        <xs:sequence>
            <xs:element name="value">
                <xs:simpleType>
                    <xs:restriction base="xs:string">
                        <xs:minLength value="1"/>
                    </xs:restriction>
                </xs:simpleType>
            </xs:element>
        </xs:sequence>
        <xs:attribute name="name" use="required">
            <xs:simpleType>
                <xs:restriction base="xs:NMTOKEN">
                    <xs:enumeration value="id"/>
                    <xs:enumeration value="client"/>
                    <xs:enumeration value="section"/>
                    <xs:enumeration value="token"/>
                </xs:restriction>
            </xs:simpleType>
        </xs:attribute>
    </xs:complexType>
</xs:schema>

This XSD forces, all the child element value to be not empty (having minlength condition as 1). I understood that it's not possible to do conditional assignments on XSD 1.0. I have tried with XSD 1.1. Following is the change that I added to XSD:

  1. Add the following NS:
<xs:schema elementFormDefault="qualified"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
           vc:minVersion="1.1">
  1. Modify the XSD as follows:
    <xs:simpleType name="mandatory">
        <xs:restriction base="xs:string">
            <xs:minLength value="1"/>
        </xs:restriction>
    </xs:simpleType>
    <xs:simpleType name="optional">
        <xs:restriction base="xs:string">
            <xs:minLength value="0"/>
        </xs:restriction>
    </xs:simpleType>

    <xs:complexType name="tagType">
        <xs:sequence>
            <xs:element name="value" type:"xs:string">
                <xs:alternative test="@name = token" type="optional"/>
                <xs:alternative test="@name != token" type="mandatory"/>
            </xs:element>
        </xs:sequence>
        <xs:attribute name="name" use="required">
            <xs:simpleType>
                <xs:restriction base="xs:NMTOKEN">
                    <xs:enumeration value="id"/>
                    <xs:enumeration value="client"/>
                    <xs:enumeration value="section"/>
                    <xs:enumeration value="token"/>
                </xs:restriction>
            </xs:simpleType>
        </xs:attribute>
    </xs:complexType>


This doesn't seem to work. Appreciate any help to do this conditional assignment on XSD.

CodePudding user response:

To use conditional type assignment, you need to make the type of the tag element conditional on the value of its attributes. Something like

<xs:element name="tag">
   <xs:alternative test="@name ='id'" type="idTagType"/>
   <xs:alternative test="@name ='client'" type="clientTagType"/>
   ...
</xs:element>

<xs:complexType name="idTagType">
  ...
</xs:complexType>

<xs:complexType name="clientTagType">
  ...
</xs:complexType>

You can make the different xxTagType definitions all inherit from an abstract super type if you want.

Incidentally, there's another way of validating this kind of structure that is often overlooked, which is particularly useful if you want it to work with XSD 1.0. That is to write your validation as a (transform -> validate) pipeline, where the transformation step converts the structure to something like:

<nodes>
   <node>
      <id>345</value>
      <client>test</value>
      <section>test2</value>
      <token>79467</value>
   </node>
</nodes>

which is much easier to validate (and much easier for subsequent processing as well).

  • Related