Page 1 of 1

Sorting and Looping within Schematron-Rule

Posted: Mon Mar 05, 2018 2:26 pm
by Thomas W
Hello Oxygen Team

I have the following XML structure and I need to validate it with Schematron so that the time periods do not overlap:

Code: Select all

<periods>
<period start='2018-02-01' end '2018-02-14' />
<period start='2018-01-01' end '2018-01-22' />
<period start='2018-04-01' end '2018-04-25' />
<period start='2018-03-01' end '2018-01-22' />
<period start='2018-04-24' end '2018-04-28' />
</periods>
So what actually needs to be done is to sort the list descending by start date, loop through the sorted list and check in every iteration whether the end date of the current period is smaller than the start date of the period from the previous iteration. That said, in the second iteration of such a loop the validation logic would check if '2018-04-25' < '2018-04-24' which is false. Thus, we could set a variable "HasOverlappings" to true in this case which is then invoked after the loop within a schematron rule check.

Any idea if this can be done in Schematron and if yes how?

Thank you very much.
Best
Thomas

Re: Sorting and Looping within Schematron-Rule

Posted: Mon Mar 05, 2018 4:35 pm
by tavy
Hello Thomas,

A solution for this is to use XSLT functions in the Schematron file. Something like the function below:

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process">

<sch:ns prefix="fct" uri="localFunctions"/>

<sch:pattern>
<sch:rule context="periods">
<sch:let name="overlapping" value="fct:checkOverlaping((),period)"/>
<sch:assert test="$overlapping=''">
Overlapping period: <sch:value-of select="$overlapping"/></sch:assert>
</sch:rule>
</sch:pattern>

<xsl:function name="fct:checkOverlaping" >
<xsl:param name="currentEndDate" as="xs:date?"/>
<xsl:param name="periods" as="node()*"/>
<xsl:choose>
<xsl:when test="not(exists($currentEndDate))">
<xsl:variable name="orderedPeriods" as="node()*">
<xsl:for-each select="$periods">
<xsl:sort select="@start"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="fct:checkOverlaping(xs:date($orderedPeriods[position()=1]/@end),$orderedPeriods[position()>1])"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="
if (not(exists($periods))) then '' else
if ($currentEndDate > $periods[position()=1]/@start)
then concat('End: ', $currentEndDate, ' Start: ', $periods[position()=1]/@start) else
fct:checkOverlaping($periods[position()=1]/@end,$periods[position()>1])"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</sch:schema>
Best Regards,
Octavian

Re: Sorting and Looping within Schematron-Rule

Posted: Mon Mar 05, 2018 6:10 pm
by Thomas W
Hi Octavian

That looks cool. Thank you very much. I will try this.

All the best
Thomas