views:

40

answers:

1

Is it possible with XML schema to restrict the depth of child elements nested in a parent?

The context here is I collect alarms from a management system and I want to provide a XML document which allows the end user define some rules in order to filter the alarms into folders in the UI. I want to restrict the depth of nested folders to 3 so an end user can't nest hundreds of levels deep - as filtering to so many levels would crash the application eventually.

I could write some code to handle this, but it seems appropriate to define this in the schema if its possible.

For example, this would be fine:

<group name="Folder 1">
        <group name="Folder 2">
            <group name="Folder 3">
                <group name="Folder 4">
                </group>
            </group>
        </group>
    </group>

This would be invalid, as Folder 5 is too deep.

<group name="Folder 1">
        <group name="Folder 2">
            <group name="Folder 3">
                <group name="Folder 4">
                    <group name="Folder 5">
                    </group>
                </group>
            </group>
        </group>
    </group>

My schema looks like this, but its not restricting the depth for the snippet above.

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

  <xs:element name="hierarchy">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="group" type="GroupType" />
      </xs:sequence>
      <xs:attribute name="name" type="xs:string" />
    </xs:complexType>
  </xs:element>

  <xs:complexType name="GroupType">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="group" type="GroupType" />
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" use="required" />
    <xs:attribute name="filterOn" type="xs:string" use="optional" />
    <xs:attribute name="operator" type="xs:string" use="optional" />
    <xs:attribute name="value" type="xs:string" use="optional" />
  </xs:complexType>
</xs:schema>

Any pointers much appreciated!

A: 

A pretty and simple solution is not available in XML Schema (but is in other languages) but you can actually to it, by nesting the whole thing yourself which I can't recommend.

So, if I were you, I would do one of two things:

  1. Pick an alternative to XML Schema (or an extension) See: http://en.wikipedia.org/wiki/XML_schema#XML_schema_languages (take a look at RELAX and Schematron)
  2. Do the depth checking in your application and reject it if depth > 3 and make the behavior clear in the documentation for your XML Schema and application.

Quote from Wiki about XML Schema version 1.1 (candidate recommendation):

The ability to define assertions against the document content by means of XPath 2.0 expressions (an idea borrowed from Schematron)

<- This will do the depth pretty easy to define.

For comment on how to represent nesting depth in XMLSchema:

Basically you can do something like the following (still recommending doing it in code). You then add attributes, adjusting depth etc. (you may be able to reuse attributes with extension or restrict but I'm not 100% sure). This method can get pretty nasty (exponential) if you allow multiple kinds of sub-elements:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema elementFormDefault="qualified" 
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://somenamespace.com"
           xmlns="http://somenamespace.com"&gt;

  <xs:element name="hierarchy">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="group" type="GroupTypeDepth0" />
      </xs:sequence>
      <xs:attribute name="name" type="xs:string" />
    </xs:complexType>
  </xs:element>

  <xs:complexType name="GroupTypeDepth0">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="group" type="GroupTypeDepth1" />
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="GroupTypeDepth1">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="group" type="GroupTypeDepth2" />
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="GroupTypeDepth2"/>
</xs:schema>

Valid:

<hierarchy xmlns="http://somenamespace.com"&gt;
  <group>
    <group>
      <group/>
    </group>
  </group>
</hierarchy>

Invalid:

<hierarchy xmlns="http://somenamespace.com"&gt;
  <group>
    <group>
      <group>
        <group/>
      </group>
    </group>
  </group>
</hierarchy>
lasseespeholt
How would I nest the schema to achieve this? Unfortunately I have to use XML schema for this as I can't add the 3rd party libraries (Java app) needed to support RELAX/Schematron due to licensing considerations. I'll probably end up having to write some very unelegant code to support this, even if the schema solution is messy, it may be the lesser of two evils.
tja
@tja see update :)
lasseespeholt
@lasseespeholt - perfect, I take your point on the elegance (or lack of).
tja