Skip to Content

The official road map is a great way to stay up to date with what’s coming to SAP Cloud Platform Integration. While perusing the current road map document, I noticed a new capability planned for Q3 of 2018: XML Schema 1.1 support.

XML Schema version 1.1 isn’t exactly brand new (it became a W3C Recommendation in 2012), but it does introduce some interesting features. In this blog, I’ll cover what I consider the most interesting one of the lot: Assertions.

Validating business rules with assertions

XML Schema 1.0 is great for describing the structure of your XML documents, and the datatypes of element content and attributes. However, it falls short if you need to express more complicated business rules. For that, you either need a rule-based schema language, such as Schematron, or to write custom validation code. Either way, document validation becomes a two-step process: one step for document structure and another for business rules.

With XML Schema 1.1, you can add business rules to your existing schema, using the new <xs:assert> element. Your assertions are written in XPath, and they are evaluated as part of the document validation.

The <xs:assert> element

Here’s a simple example of the new <xs:assert> element, that I borrowed from the XML Schema 1.1 W3C Recommendation:

<xs:complexType name="intRange">
   <xs:attribute name="min" type="xs:int"/>
   <xs:attribute name="max" type="xs:int"/>
   <xs:assert test="@min le @max"/>
</xs:complexType>

An element of type intRange has a min and a max integer attribute. The assertion ensures that the beginning of the range is less than or equal to the end of the range. A sensible rule, that is easy to implement in XML Schema 1.1.

The test itself is contained in the test attribute of the <xs:assert> element. Tests are XPath 2.0 expressions, that must evaluate to either true or false. If a test expression does not evaluate to a boolean value, it will be converted automatically, as if it had been wrapped in an fn:boolean call.

Your test expression can access the contents and attributes of the element, that the assertion belongs to, and the descendants of that element. In other words: you can’t access siblings and ancestors of that element. You’ll see an example of this in the next section.

Taking XML Schema 1.1 assertions for a spin

Let’s take a look at a more complete example. Here’s an XML document containing a simplified conference agenda:

<?xml version="1.0" encoding="UTF-8"?>
<agenda>
   <item type="registration" startTime="08:00:00" endTime="09:00:00">Registration</item>
   <item type="keynote" startTime="09:00:00" endTime="09:30:00">Opening keynote</item>
   <item type="session" startTime="09:30:00" endTime="10:30:00">First interesting session of the day</item>
   <item type="break" startTime="10:30:00" endTime="10:45:00">Short coffee break</item>
   <item type="session" startTime="10:45:00" endTime="11:45:00">Another really interesting session</item>
   <item type="session" startTime="11:45:00" endTime="12:30:00">A slightly shorter session</item>
   <item type="break" startTime="12:30:00" endTime="13:30:00">Lunch break</item>
   <item type="session" startTime="13:30:00" endTime="14:30:00">First session after lunch</item>
   <item type="break" startTime="14:30:00" endTime="14:45:00">Afternoon coffee break</item>
   <item type="session" startTime="14:40:00" endTime="15:30:00">Another slightly shorter session</item>
   <item type="session" startTime="15:30:00" endTime="16:30:00">Last session of the day</item>
</agenda>

The agenda consists of items, such as sessions and breaks. Each item has a title, a type, a start time and an end time. Focusing only on the structure and the datatypes, here’s one take on an XML Schema for validating agendas:

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

  <xs:element name="agenda" type="agendaItem"/>

  <xs:complexType name="agendaItem">
    <xs:sequence>
      <xs:element name="item" minOccurs="1" maxOccurs="unbounded">
        <xs:complexType mixed="true">
          <xs:simpleContent>
            <xs:extension base="xs:string">
              <xs:attribute name="endTime" use="required" type="xs:time"/>
              <xs:attribute name="startTime" use="required" type="xs:time"/>
              <xs:attribute name="type" use="required" type="itemType"/>
            </xs:extension>
          </xs:simpleContent>          
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
  
  <xs:simpleType name="itemType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="registration" />
      <xs:enumeration value="keynote" />
      <xs:enumeration value="session" />
      <xs:enumeration value="break" />
    </xs:restriction>
  </xs:simpleType>

</xs:schema>

Let’s convert this to XML Schema 1.1 and add validation of the following two business rules:

  • An agenda item’s end time must be greater than its start time
  • For all items except the first one, the start time must be equal to the previous item’s end time

The first business rule is simple to implement using an XML Schema 1.1 assertion added on the <item> level:

<xs:assert test="@startTime lt @endTime"/>

The second business rule is a bit more complicated. Since it involves comparing an attribute of an <item> element with an attribute of a sibling <item> element, the assertion cannot be added at the <item> level. Instead, we add it at the <agenda> level. Here’s the complete assertion:

<xs:assert test="every $item in item satisfies (not($item/preceding-sibling::item) or $item/@startTime eq $item/preceding-sibling::item[1]/@endTime)"/>

The test expression states that an <item> element must either be the first item (not($item/preceding-sibling::item)) or its startTime attribute must match the endTime attribute of the previous item ($item/@startTime eq $item/preceding-sibling::item[1]/@endTime).

Adding the assertions to the original schema, and updating the XML Schema version to 1.1, we end up with the following schema:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" vc:minVersion="1.1" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning">

    <xs:element name="agenda" type="agendaItem"/>
        
    <xs:complexType name="agendaItem">
        <xs:sequence>
            <xs:element name="item" minOccurs="1" maxOccurs="unbounded">
                <xs:complexType>
                    <xs:simpleContent>
                        <xs:extension base="xs:string">
                            <xs:attribute name="endTime" use="required" type="xs:time"/>
                            <xs:attribute name="startTime" use="required" type="xs:time"/>
                            <xs:attribute name="type" use="required" type="itemType"/>
                            <xs:assert test="@startTime lt @endTime"/>
                        </xs:extension>
                    </xs:simpleContent>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
        <xs:assert test="every $item in item satisfies (not($item/preceding-sibling::item) or $item/@startTime eq $item/preceding-sibling::item[1]/@endTime)"/>
    </xs:complexType>
    
    <xs:simpleType name="itemType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="registration" />
            <xs:enumeration value="keynote" />
            <xs:enumeration value="session" />
            <xs:enumeration value="break" />
        </xs:restriction>
    </xs:simpleType>
    
  </xs:schema>

To perform the validation, you need an XML editor or a validation library that supports XML Schema 1.1. I used the Oxygen XML editor, and lo and behold: the penultimate agenda item has a start time that does not match the end time of the previous item! XML Schema 1.1 assertions prevented the embarrassment of publishing a faulty conference agenda 🙂

A word on custom error messages

At the moment, there is no standard way of customising the error message generated by a failed assertion. However, some validation engines do provide support for custom error messages. Saxon, for instance, lets you add your own error messages in the Saxon-specific message attribute:

<xs:assert
   xmlns:saxon="http://saxon.sf.net/"
   saxon:message="An agenda item's end time must be greater than its start time"
   test="@startTime lt @endTime"
/>

It remains to be seen whether Cloud Integration will offer something similar.

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

  1. Fatih Pense

    Thanks for, as usual, the informative post, Mr. Morten!

    It looks like 2018 will be the year CPI matures and becomes a mainstream product. I’m excited about the XI adapter, exactly once feature and Management APIs.

    (1) 

Leave a Reply