restriction of elements based on another attribute using XSD 1.1

maraz picture maraz · Aug 1, 2014 · Viewed 7.3k times · Source

I am trying to create a schema definition using XSD 1.1 in which the number of other elements is dependent on the attribute of another element. E.g. The number of the BaPath elements BaPath depends on the value of the attribute "service" of the "Conn" element. The xsd I wrote is

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsd:element name="Mapping">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="Link" minOccurs="0" maxOccurs="unbounded" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="Env">
        <xsd:complexType>
            <xsd:attribute name="name" use="required">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:string">
                        <xsd:enumeration value="UTEST" />
                        <xsd:enumeration value="TEST" />
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:attribute>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="Link">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="Conn" maxOccurs="unbounded" />
            </xsd:sequence>
            <xsd:attribute name="service" use="required">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:string">
                        <xsd:enumeration value="FILESNF" />
                        <xsd:enumeration value="MSGSNF" />
                        <xsd:enumeration value="MSGRT" />
                        <xsd:enumeration value="FILERT" />
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:attribute>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="Conn">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="BaPath" maxOccurs="unbounded" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="BaPath">
        <xsd:complexType>
            <xsd:attribute name="flow" use="required">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:string">
                        <xsd:assertion test="@service eq 'MSGRT'">
                            <xsd:enumeration value="TRS" />
                            <xsd:enumeration value="ZTRS" />
                        </xsd:assertion>
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:attribute>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="Dep">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="Env" maxOccurs="unbounded" />
                <xsd:element ref="Mapping" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

    <xsd:complexType name="CfgType">
        <xsd:sequence>
            <xsd:element ref="Dep" />
        </xsd:sequence>
    </xsd:complexType>

    <xsd:element name="Cfg" type="CfgType"></xsd:element>

</xsd:schema>
</Cfg>

for example if Conn element has an attribute service eq 'MSGRT' there must be 2 BaPath elements with attributes TRS and ZTRS

<Cfg xmlns="http://www.alpha.com/beta">
  <Dep>
    <Env name="UTEST"/>
    <Mapping>
      <Link t2s_service="MSGRT">
        <Conn>
          <BaPath flow="ZTRS"/>
          <BaPath flow="TRS"/>
        </Conn>
      </Link>
    </Mapping>
  </Dep>

if service eq 'FILESNF' of Conn there must 3 BaPath elements with attributes FTS, ZFTS and MSSDN

I tried different solutions but no one seems to work. Is it possbile to solve this problem with assertion of xsd-1.1?

Answer

sergioFC picture sergioFC · Aug 2, 2014

You could use asserts in the Link element (and a enumeration with all the values in the flow attribute).

<xsd:element name="Link">
    <xsd:complexType>
        <xsd:sequence>
            <xsd:element ref="Conn" maxOccurs="unbounded"/>
        </xsd:sequence>
        <xsd:attribute name="service" use="required">
            <xsd:simpleType>
                <xsd:restriction base="xsd:string">
                    <xsd:enumeration value="FILESNF"/>
                    <xsd:enumeration value="MSGSNF"/>
                    <xsd:enumeration value="MSGRT"/>
                    <xsd:enumeration value="FILERT"/>
                </xsd:restriction>
            </xsd:simpleType>
        </xsd:attribute>
        <xsd:assert test="(@service ne 'MSGRT') or (count(Conn[count(BaPath[@flow eq 'TRS']) eq 1 and count(BaPath[@flow eq 'ZTRS']) eq 1 and count(BaPath) eq 2]) eq count(Conn))"></xsd:assert>
        <xsd:assert test="(@service ne 'FILESNF') or (count(Conn[count(BaPath[@flow eq 'FTS']) eq 1 and count(BaPath[@flow eq 'MSSDN']) eq 1 and count(BaPath[@flow eq 'ZFTS']) eq 1 and count(BaPath) eq 3]) eq count(Conn))"></xsd:assert>
    </xsd:complexType>
</xsd:element>

Explanation of one of the assert (the other asserts would be similar):

(@service ne 'MSGRT') or (count(Conn[count(BaPath[@flow eq 'TRS']) eq 1 and count(BaPath[@flow eq 'ZTRS']) eq 1 and count(BaPath) eq 2]) eq count(Conn))

First we check the service attribute. Then, using Conn[count(BaPath[@flow eq 'TRS']) eq 1 and count(BaPath[@flow eq 'ZTRS']) eq 1 and count(BaPath) eq 2] we are selecting all the Conn element of the Link that have exactly two BaPath childs (one with flow=TRS and other with flow=ZTRS). After that we check that all of the Conn elements pass that restriction.

So, using that, this example will be valid:

<Link service="MSGRT">
    <Conn>
        <BaPath flow="TRS"></BaPath>
        <BaPath flow="ZTRS"></BaPath>
    </Conn>    
</Link>

This example will not be valid:

<Link service="MSGRT">
    <Conn>
        <BaPath flow="MSSDN"></BaPath>
        <BaPath flow="ZTRS"></BaPath>
    </Conn>    
</Link>

This example will not be valid:

<Link service="MSGRT">
    <Conn>
        <BaPath flow="TRS"></BaPath>
    </Conn>    
</Link>

This example will not be valid:

<Link service="MSGRT">
    <Conn>
        <BaPath flow="TRS"></BaPath>
        <BaPath flow="ZTRS"></BaPath>
        <BaPath flow="ZFTS"></BaPath>
    </Conn>    
</Link>

Edit:

Another option it's to use conditional type alternatives (example here), but you would probably need to duplicate parts of your Schema.