The Schematron

An XML Structure Validation Language using Patterns in Trees

Purchase Order Example

The following example is taken from http://www.w3.org/TR/xmlschema-1/ (You can see that the Schematron schemas do not have type as a first-class object; however type constructs can be modeled and specific messages can be given using a type-based analysis. The following schema does not include checks of date formats, etc, however is it possible that these could be made. Also, checks on positions could be put in, e.g. that a name should be followed by a street.)

Example
<?xml version='1.0'?>
<PurchaseOrder orderDate="1999-05-20">
    <shipTo type="US">
        <name>Alice Smith</name>
        <street>123 Maple Street</street>
        <city>Mill Valley</city>
        <state>CA</state>
        <zip>90952</zip>
    </shipTo>
    <shipDate>1999-05-25</shipDate>
    <comment>Get these things to me in a hurry, my lawn is going wild!</comment>
    <Items>
        <Item pno="333-333">
            <productName>Lawnmower, model BUZZ-1</productName>
            <quantity>1</quantity>
            <price>148.95</price>
            <comment>Please confirm this is the electric model</comment>
        </Item>
        <Item pno="444-444">
            <productName>Baby Monitor, model SNOOZE-2</productName>
            <quantity>1</quantity>
            <price>39.98</price>
        </Item>
    </Items>
</PurchaseOrder>

A corresponding Schematron schema (without any lexical checking, which could be added) is

<schema>
	<!-- Schematron structural schema to validate purchase orders -->
	<!-- Note: content models are treated as "open". -->
	<title>Schema for Purchase Order Example</title>
	<!-- First, simulate types -->
	<pattern name="address type" >
		<rule context="name| city | state | zip">
		<!-- This rule couples the elements of the address type together, so that
			if one appears anywhere, the others must also. -->
			<assert test="parent::*/street"
			>An address should have a street.</assert>
			<assert test="parent::*/city"
			>An address should have a city.</assert>
			<assert test="parent::*/state"
			>An address should have a state.</assert>
			<assert test="parent::*/zip"
			>An address should have a zip.</assert>
			<assert test="parent::*/@type"
			>An address should have a type attribute.</assert>
		</rule>
		<rule context="/name | /city | /state | /zip">
			<report test="self::*"
			>The elements <name/> is not expected at
			the top of a document. They form part of an address.</report>
		</rule>
	</pattern>
	<pattern name="purchase order type">
		<rule context="shipTo | shipDate | Items">
		<!-- This rule couples the elements of the address type together, so that
			if one appears anywhere, the others must also. -->
			<assert test="parent::*/shipTo"
			>A purchase order should have a shipTo element.</assert>
			<assert test="parent::*/shipDate"
			>A purchase order should have a shipDate element.</assert>
			<assert test="parent::*/Items"
			>A purchase order should have an Items element.</assert>
			<assert test="parent::*/@orderDate"
			>A purchase order should have an orderDate attribute.</assert>
		</rule>
		<rule context="/shipTo | /shipDate | /Items">
			<report test="self::*"
			>The element <name/> is not expected
			at the top of a document. It should be in
			the body of a purchase order.</report>
		</rule>
	</pattern>
	<!-- Next, the specific elements -->
	<pattern name="Specific Elements">
		<rule context="PurchaseOrder">
			<assert test="shipTo"
			>A purchase order element should have a shipping address.
			There should be a shipTo element contained by the PurchaseOrder element.</assert>
		</rule> 
		<rule context="shipTo">
			<assert test="name"
			>A shipping address element should have a name element.</assert>
			<!-- The rest of the content model is tested through the
				addressType pattern. -->
		</rule> 
		<rule context="Items">
			<assert test="count(child::*) = count(child::Item)"
			>The Items element can only contain Item elements</assert>
		</rule>
		<rule context="Item">
			<assert test="productName"
			>The Item element should contain a product name. </assert>
			<assert test="quantity"
			>The Item element should contain a quantity element </assert>
			<assert test="price"
			>The Item element should contain a price element.  </assert>
		</rule> 
		<rule context="comment">
			<report test="child::*"
			>A comment should have no subelements</report >
			<assert test="parent::Item | parent::PurchaseOrder " 
			>A comment can only appear in an Item or a Purchase Order</assert>
		</rule>
	</pattern>
</schema>

The schema can be found here. A pretty-printed version can be found here.


Copyright (C) Rick Jelliffe, Academia Sinica Computing Centre. The Schematron software and this page are available for any public use, under the conditions of the GPL or MPL, but please mention our names in any documentation or About screens for any products that uses it. Comments, fixes and upgrades welcome: email ricko@gate.sinica.edu.tw