Search code examples
xmlxsdxml-validation

Attributes based on parent element


I have a tree structure I want to create a schema for. Each node have two list of nodes under them. These list can the same types of elements. However elements in one list requires two additional attributes Is there any way to require attributes on an element based on it's parent.

I have made an attempt by creating separate list types. One list type simply contains a choice of elements. The other contains the same choice type, but each type is extended to require the extra parameters. This works and it's pretty simple, however it seems needlessly verbose, and I am wondering if there is a smarter solution. Currently the schema looks like this:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.example.org/ElementSchema" xmlns="http://www.example.org/ElementSchema"
    elementFormDefault="qualified">

    <!--BaseElementTypes -->
    <xs:element name="BaseElement" type="BaseElementType" />
    <xs:element name="DoubleElement" type="DoubleElementType" />
    <xs:element name="BoundedDoubleElement" type="BoundedDoubleElementType" />

    <!--ElementListTypes -->
    <xs:complexType name="ChildListType">
        <xs:choice minOccurs="1" maxOccurs="unbounded">
            <xs:element ref="BaseElement" />
            <xs:element ref="DoubleElement" />
            <xs:element ref="BoundedDoubleElement" />
        </xs:choice>
    </xs:complexType>

    <xs:complexType name="PropertyListType">
        <xs:choice minOccurs="1" maxOccurs="unbounded">
            <xs:element name="BaseElement">
                <xs:complexType>
                    <xs:complexContent>
                        <xs:extension base="BaseElementType">
                            <xs:attribute name="Role" type="xs:string" use="required" />
                            <xs:attribute name="Tag" type="xs:integer" use="required" />
                        </xs:extension>
                    </xs:complexContent>
                </xs:complexType>
            </xs:element>
            <xs:element name="DoubleElement">
                <xs:complexType>
                    <xs:complexContent>
                        <xs:extension base="DoubleElementType">
                            <xs:attribute name="Role" type="xs:string" use="required" />
                            <xs:attribute name="Tag" type="xs:integer" use="required" />
                        </xs:extension>
                    </xs:complexContent>
                </xs:complexType>
            </xs:element>
            <xs:element name="BoundedDoubleElement">
                <xs:complexType>
                    <xs:complexContent>
                        <xs:extension base="BoundedDoubleElementType">
                            <xs:attribute name="Role" type="xs:string" use="required" />
                            <xs:attribute name="Tag" type="xs:integer" use="required" />
                        </xs:extension>
                    </xs:complexContent>
                </xs:complexType>
            </xs:element>
        </xs:choice>
    </xs:complexType>


    <!--elementTypes -->
    <xs:complexType name="BaseElementType">
        <xs:sequence>
            <xs:element name="Children" type="ChildListType" minOccurs="0" />
            <xs:element name="Properties" type="PropertyListType"
                minOccurs="0" />
        </xs:sequence>
        <xs:attribute name="ID" type="xs:positiveInteger" use="required" />
        <xs:attribute name="Key" type="xs:string" use="required" />
    </xs:complexType>

    <xs:complexType name="DoubleElementType">
        <xs:complexContent>
            <xs:extension base="BaseElementType">
                <xs:sequence>
                    <xs:element name="Value" type="xs:float" />
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="BoundedDoubleElementType">
        <xs:complexContent>
            <xs:extension base="DoubleElementType">
                <xs:sequence>
                    <xs:element name="DesiredValue" type="xs:float" />
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>
</xs:schema>

Specifically I would like to know if there is a better way to achieve the same result as the PropertyListType?


Solution

  • Is there any way to require attributes on an element based on its parent.

    In XSD 1.0, no.

    In XSD 1.1, yes: Make the attributes optional and then xs:assert that every child of the particular parent element does have the extra attributes. Here is an example showing how to use an every..satisfies assertion in XSD 1.1.