sellarafaeli
sellarafaeli

Reputation: 1177

In XSD, can I set mandatory child elements based on parent element's content?

I have an XML & XSD coupling which includes 'House' elements similar to the following:

<root>
...
<House>
    <id>1</id>
    <type>Cottage</type>
    <imageSrc> image.jpg </imageSrc>
</House>
<House>
    <id>2</id>
    <type>Private</type>
</House>    

I want to enforce via my XSD that all Houses of type "Cottage" must have a mandatory tag, but other houses do not. (Or the other way aruond - the only 'Privates' do not. Is there a way to do this?

Upvotes: 0

Views: 1675

Answers (2)

tom redfern
tom redfern

Reputation: 31780

Although it is true that XSD does not support this behavior you can simply define two House types, one with the mandatory node and one without.

Upvotes: 0

Martin Rist
Martin Rist

Reputation: 101

You might want to consider using something like Schematron to express these additional validation rules that are beyond the capabilities of XML Schema. Schematron is an open (ISO) standard which allows you to express assertions about patterns that should be present in XML documents. For example, you can use it to express constraints such as:

  • if element A is present, element B is prohibited. If A and B are siblings, you could do this using an xs:choice in XML schema, but this approach doesn't work in XML Schema if the two elements aren't siblings.
  • if element A has an attribute with a specific value, then it must contain child element B.
  • element A must be equal to the total of elements B and C.

In your specific example, I'm going to assume that imageSrc is the mandatory tag whose presence you want to mandate on Houses of type 'Cottage' and prohibit on others.

An example of a simple Schematron schema to express these assertions is:

<schema xmlns="http://purl.oclc.org/dsdl/schematron">
  <pattern>
    <!-- Houses of type 'Cottage' must contain an imageSrc element -->
    <rule context = "/root/House[type/text() = 'Cottage']">
      <assert test = "imageSrc">Cottages must have an image</assert>
    </rule>

    <!-- Houses not of type 'Cottage' must not contain an imageSrc element -->
    <rule context = "/root/House[type/text() != 'Cottage']">
      <assert test = "not(imageSrc)">Non-cottages must not have an image</assert>
    </rule>
  </pattern>
</schema>

Here, I've expressed the pattern as two separate rules, one for each of the two parts of your requirement.

Each rule has a context attribute which is an XPath expression. When this expression evaluates to true, the rule 'fires'. So, the first rule fires on House elements with a type child element whose text content is equal to 'Cottage'.

When the rule fires, the assertion is then checked, by evaluating the test attribute of the assert element. Again, this is an XPath expression, but the context node for evaluating the test expression is the node matched by the rule element (in this case House). When the test expression evaluates to true, the assertion passes (i.e. the document is valid with respect to this assertion). If it evaluates to false, the assertion fails. The text content of the assert element is typically a human-readable description of the assertion that could be passed onto end users. Schematron also offers additional facilities (called 'diagnostics') to link additional technical messages to assertions.

A publicly-available XSLT-based implementation of Schematron is available at http://www.schematron.com/implementation.html. A typical validation pipeline would involve first validating the document against an XML Schema that defines the basic structural requirements for the document (e.g. element hierarchy, cardinalities, data types, patterns etc). Then, once the document has passed this basic level of validation, you would then validate the document against the Schematron schema to validate the additional pattern-based constraints. This pipeline could be implemented programatically, or using (for example) batch files, Ant scripts or an XProc processor.

Upvotes: 2

Related Questions