Skip to main content

 Sample Code

Some code samples

Schematron sample code

This article contains some general Schematron code samples to enable a quick start when writing Schematron validation code in PageSeeder.

For more specific code samples, see:

Basic Schematron

The following is a boilerplate Schematron schema. It has placeholders for different types of validation rules.

<?xml version="1.0"?>
<!--   A sample schematron -->

<sch:schema 
     xmlns:sch="http://purl.oclc.org/dsdl/schematron"
         title="Rules for [type] documents">

            <!-- You can creates as many patterns
                                as you like -->
      <sch:pattern name="[your name]">

            <!-- Rules matchings an element in
                              your document -->
      <sch:rule context="[your XSLT pattern]">

            <!-- Reported as an info if
                          test is true -->
      <sch:report test="[your xpath test]">
                         [your message]
                                </sch:report>

            <!-- Failed assertions are reported
                                 as errors -->
       <sch:assert test="[your xpath]">
                         [your message]
                                </sch:assert>

            <!-- Add more <sch:report>
                         or <sch:assert>... -->

         </sch:rule>
            <!-- Add more <sch:rule> ... -->

    </sch:pattern>
            <!-- Add more <sch:pattern> ... -->

</sch:schema>

Example Schema

The following schema is designed to validate some generic best practice rules for PSML.

<?xml version="1.0"?>

<!--  This schematron validates a PSML document.

      The schematron rules can be used to enforce
      additional constraints required by the
      application.

      see https://dev.pageseeder.com/api/data/psml.html
  -->

<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"
            title="Rules for tutorial documents">

  <!-- Set of rules applying to the entire document -->
  <sch:pattern name="Document">
    <sch:rule context="/document">

      <!-- Too many sections -->
      <sch:assert test="count(section) le 20">This
        document contains over 20 sections. Consider
        whether to split it into multiple documents.
      </sch:assert>

      <!-- No Doc ID -->
      <sch:report test="not(documentinfo/uri/@docid)">
       This document does not have a Doc ID.
      </sch:report>

      <!-- No Label -->
      <sch:report test="not(documentinfo/uri/@labels)">
       This document has no label.</sch:report>

    </sch:rule>
  </sch:pattern>

  <!-- Validation rules for document fragments  -->
  <sch:pattern name="Fragments">
    <sch:rule context="section/fragment">

      <!-- Fragment has no heading -->
      <sch:assert test="name(*[1]) = 'heading'">
        Fragment '<sch:value-of select="@id"/>'
        has no title.</sch:assert>

      <!-- Fragment is too long (over 2000 chars)-->
      <sch:assert test="string-length(string-join(.//text(), ''))
                        le 2000">
        Fragment '<sch:value-of select="@id"/>'
        has over 2000 characters.
      </sch:assert>

    </sch:rule>
  </sch:pattern>
</sch:schema>

Check cross-referenced document

A rule to check the content of an xref target document.

<!-- Match cross references -->
<sch:rule context="xref|blockxref">

  <!-- Check referenced document contains a heading2 -->
  <sch:assert test="document(@href)//heading[@level='2']">
    Document <sch:value-of select="@href"/> has no heading2.
  </sch:assert>

 </sch:rule>

Check terms used in a publication

A schematron which when run on a publication root document will check which defined terms are used in the publication. Requires PageSeeder 5.9800 or higher.

<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">

  <sch:let name="usage"
    value="document(concat(
           'ps:search?filters=pstype:document&amp;pspublicationid:',
           /document/documentinfo/publication/@id,
           '&amp;facets=psinline-definition',
           '&amp;facetsize=1000&amp;pagesize=1'))" />
  <sch:pattern>    
    <sch:title>Usage</sch:title>
    <sch:rule context="/">
 
      <sch:let name="terms"
               value="$usage//facets/facet/term" />
   
      <sch:report test="$terms">
        The following <sch:value-of select="count($terms)" />
        defintions are used in this publication:
        <sch:value-of select="string-join(sort($terms/@text,
'http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive')
                              ,', ')"/>
      </sch:report>     
    </sch:rule>
  </sch:pattern>
</sch:schema>

Check documents assigned to me

A schematron to check which documents are assigned to me and have status Complete or In Progress. Requires PageSeeder 5.9811 or higher.

<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
  
  <sch:let name="self" value="document('ps:self')" />
  <sch:let name="memberid" value="$self/member/@id" />
  <sch:let name="workflow" value="document('ps:workflow')" />
  <sch:pattern>
    <sch:title>Document</sch:title>
    <sch:rule context="/document">

      <!-- Complete status -->
      <sch:assert id="COMPLETE_STATUS" flag="info"
                  test="not(@status='Complete' and
                    $workflow/workflow/assignedto[@id=$memberid])">
        Document is assigned to you and COMPLETE.</sch:assert>

      <!-- In Progress status -->
      <sch:assert id="IN_PROGRESS_STATUS" flag="info"
                  test="not(@status='In Progress' and
                    $workflow/workflow/assignedto[@id=$memberid])">
        Document is assigned to you and IN PROGRESS.</sch:assert>
    </sch:rule>
  </sch:pattern>
</sch:schema>

Debug Schematron code

If you need to debug Schematron and you're not sure what your getting in a variable, you can used code similar to this:

<!-- A separate pattern helps you avoid any side-effect -->
<sch:pattern name="DEBUG">

  <!-- Have a rule that matches what you want, the document node always matches -->
  <sch:rule context="/">

    <!-- A true() test on a report always matches  -->
    <sch:report test="true()">

      <!-- The serialize function serializes the nodes as a string -->
      <sch:value-of select="serialize($variable-to-check)"/>

    </sch:report>
  </sch:rule>   
</sch:pattern>

Quickfix

A schematron to identify and fix headings that contain markup such as formatting. Requires PageSeeder 6.0 or higher.

<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
  
  <!-- Patterns -->
  <sch:include href="../../schema/pattern/heading_no_markup.sch" />

  <!-- Properties -->
  <sch:include href="../../schema/properties.sch" />

</sch:schema>

schema/pattern/heading_no_markup.sch

<sch:pattern id="heading_no_markup"
             xmlns:sch="http://purl.oclc.org/dsdl/schematron">
  <sch:title>No markup in heading</sch:title>

  <sch:rule context="fragment">
    <sch:assert test="not(heading) or heading[not(*)]" subject="heading"
                properties="fragment remove-markup-from-heading">
      Heading should not contain markup
    </sch:assert>
  </sch:rule>

</sch:pattern>

schema/properties.sch

<sch:properties xmlns:sch="http://purl.oclc.org/dsdl/schematron">

  <!-- Allows PageSeeder to identify the fragment and show in interface -->
  <sch:property id="fragment" ><sch:value-of
      select="ancestor-or-self::fragment/@id"/></sch:property>

  <!-- Quick fix -->
  <sch:property id="remove-markup-from-heading" role="quickfix">
    <description>Remove the markup from the heading</description>
  </sch:property>

</sch:properties>

quickfix/remove-markup-from-heading.xsl

<xsl:stylesheet version="2.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                exclude-result-prefixes="#all">

  <xsl:template match="/fragment">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates mode="copy"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*" mode="copy">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates mode="#current"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="heading/*" mode="copy">
    <xsl:apply-templates mode="#current"/>
  </xsl:template>

</xsl:stylesheet>
Created on , last edited on