Saxon Jing

Saxon Jing

Saxon Jing is a small open source Java library bridging the Saxon Processor with James Clark's Jing Validator enabling one to validate XML documents against Compact Syntax RelaxNG Schemas in XSLT and XQuery via XPath Functions.

If you need to validate thousands of documents against a RelaxNG Schema or need to add RelaxNG validation into your existing XML based workflow this could come in handy.

1. XSLT and XQuery Function Signatures

declare namespace rng = "http://relaxng.org/ns/structure/1.0";

declare function rng:schema(
  $uri as xs:string
) as function(node()) as empty-sequence() external;

declare function rng:schema-report(
  $uri as xs:string
) as function(node()) as element(report) external;

The $uri parameter can either be a path to a RelaxNG Schema on disk (relative or absolute) or a fully qualified URI to a resource, for instance a HTTP URL or a JAR reference.

Calling either one of these functions will fetch and compile the RelaxNG Schema and subsequently return a function which takes either a document-node() or an element() node.

This returned function is essentially a validator which you can use as many times as you like over as many documents as you like within the same XSLT or XQuery process. The Schema is only compiled once!

The function produced from rng:schema returns an empty-sequence() if validation against the node was successful but will throw an error if the document was invalid.

The function produced from rng:schema-report will return a report element which details all validation issues (if any) that occurred whilst validating a document, exactly what the errors were and where they were found. This function should never throw an error and a successful validation will simply produce an empty <report /> element.

A failed report element, will look something along the lines of:

<report>
  <error>
    <message column-number="20" line-number="2">
      character content of element "date" invalid; must be an ISO date
    </message>
    <message column-number="24" line-number="3">
      character content of element "integer" invalid; must be an integer
    </message>
    <message column-number="10" line-number="4">
      element "blurb" not allowed anywhere; expected element "text"
    </message>
    <message column-number="8" line-number="5">
      element "data" incomplete; missing required element "text"
    </message>
  </error>
</report>

2. Example XSLT stylesheet

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:rng="http://relaxng.org/ns/structure/1.0"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  exclude-result-prefixes="rng fn"
  version="3.0">
  
  <xsl:template name="xsl:initial-template">
    
    <!-- Validate!, returns empty-sequence() OR throws error -->
    <xsl:variable
      name="schema"
      as="function(node()) as empty-sequence()"
      select="rng:schema('my-schema.rnc')" />
    
    <xsl:sequence select="$schema(fn:doc('hello-world.xml'))" />

    <!-- Report!, returns report element -->
    <xsl:variable
      name="report"
      as="function(node()) as element(report)"
      select="rng:schema-report('my-schema.rnc')" />
    
    <xsl:sequence select="$report(fn:doc('hello-world.xml'))" />
    
  </xsl:template>
  
</xsl:stylesheet>

There are more examples for both XSLT and XQuery at: https://github.com/cfoster/saxon-jing/tree/master/examples

3. Requirements to run

3 libraries or jar files need to exist on the classpath.

4. Running from the Command Line

When running Saxon directly from the command line to run either XSLT or XQuery, you need to tell Saxon how to find the XPath functions via the -init argument.

Consider the following example:

java -cp jing-20091111.jar;saxon9ee.jar;saxon-jing-0.9.2.jar \
  net.sf.saxon.Transform -init:net.cfoster.saxonjing.JingInitializer \
  -s:my-source.xml -xsl:my-stylesheet.xsl -o:out.xml

5. Running from Java code

If you wish to use Saxon's S9 API to invoke XSLT or XQuery which has access to Saxon Jing, you will need to register the External Functions like so:

// imports
import net.sf.saxon.s9api.Processor;
import net.cfoster.saxonjing.SchemaFunction;
import net.cfoster.saxonjing.SchemaReportFunction;

// key code
Processor proc = new Processor(false);
proc.registerExtensionFunction(new SchemaFunction());
proc.registerExtensionFunction(new SchemaReportFunction(proc));

There are Scala tests which invoke XSLTs which are Jing Aware in the project's test suite.

6. Other ways of invoking from Java

You can let Saxon know about the Jing XPath functions in a configuration file, or by subclassing net.sf.saxon.Transform or net.sf.saxon.Query, there are quite a few approaches. Please refer to the Saxon Documentation for further details.

7. Running from <oXygen/> XML Editor

To run from oXygen, you will need to add both the Jing and Saxon Jing .jar libraries as Extensions to your XSLT or XQuery Scenario as well as setting the Initializer Class to "net.cfoster.saxonjing.JingInitializer".

Saxon Jing Extensions in oXygen

Saxon Jing Initializer in oXygen

8. Error Codes

Error CodeReason
RNGE0001Jing can not be found on Classpath
RNGE0002Syntax Error in Relax NG Schema
RNGE0003Could not find Relax NG Schema
RNGE0004Generic Exception whilst trying to load RelaxNG Schema
RNGE0005Schema Validation Failed
RNGE0006Unknown Exception

9. Source Code and Licence

Source is available on GitHub at https://github.com/cfoster/saxon-jing. Contributations are welcome!

This software is released under the Apche 2.0 Licence.