News and noteworthy - phax/ph-schematron GitHub Wiki

Releases

v10.1.0 - 2026-09-22

  • The Saxon Processor objects of the pure engines are now secured too - ph-schematron-pure-xpath and ph-schematron-pure-xslt talk to Saxon through the s9api instead of through JAXP, so they never went through SchematronTransformerFactory and consequently had no security constraints at all: doc(), document() and unparsed-text() happily dereferenced any remote URL (Server Side Request Forgery). A cross-engine test (SchematronRemoteAccessTest in ph-schematron-it) now runs a local HTTP server and asserts that no engine performs a single outbound request for any of those functions, and that a local resource is still resolved by all of them.
    • Added SchematronProcessorFactory in ph-schematron-api - the s9api counterpart of SchematronTransformerFactory. SchematronProcessorFactory.createProcessor () returns a net.sf.saxon.s9api.Processor with the Schematron security defaults applied, getDefault () returns the shared one that all engines use unless a Processor is configured explicitly, and makeProcessorSecure (Processor, String...) applies the defaults to an existing one. It offers the same customization surface as its JAXP sibling: setAllowExternalFunctions (boolean), setAllowedRemoteSchemes (String...) and setProcessorCustomizer (Consumer<Processor>), the latter being invoked last so that it can also relax a setting again.
    • Added SaxonSecureResourceResolver in ph-schematron-api - a Saxon ResourceResolver that denies all remote URL schemes. It blocks exactly the schemes that ph-commons' XMLResourceSchemeHelper considers remote (http, https, ftp and ftps), so the pure engines and the XSLT based engines now agree on what "remote" means. Local schemes (file, jar, OSGi bundle, ...) are untouched, and a nested JAR URL such as jar:http://host/x.jar!/y.xml is resolved against its inner scheme, so it cannot be used as a bypass. A blocked URI raises an XPathException rather than returning null, because null is Saxon's signal for "not handled" and would let Saxon fetch the URI itself.
    • Incompatible change: the Processor objects created by XPathConfigBuilder (including the public constant XPathConfigBuilder.DEFAULT_PROCESSOR), by XQueryAsXPathFunctionConverter, by SchematronPureXsltConfig.Builder and by SchematronResourcePureXslt.Builder now come from SchematronProcessorFactory, so they
      • deny all remote resource access of doc(), document(), unparsed-text(), collection(), json-doc(), fn:transform() and xsl:source-document,
      • disable external functions (FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS), which is what Saxon does for FEATURE_SECURE_PROCESSING - see the ph-commons entry below for the consequences. Extension functions registered via XPathConfigBuilder.setExtensionFunctions (...) or Processor.registerExtensionFunction (...) are considered trusted by Saxon and keep working,
      • and honour SchematronTransformerFactory.setAllowXInclude (boolean), so that one switch now covers every engine. A Processor that is passed in explicitly - SchematronResourcePureXslt.builder (...).processor (...) or XPathConfigBuilder.setProcessor (...) - is not touched, so an application that brings its own Saxon configuration keeps full control (and full responsibility).
    • Not changed, because it was already safe: no engine expands an external entity of an XML instance that is handed in as a javax.xml.transform.Source.
  • Bugfix: the SchematronPureXsltCache never produced a cache hit across two SchematronResourcePureXslt instances. Its cache key contained System.identityHashCode of the Saxon Processor, while every builder allocated a fresh Processor - so every resource compiled its own copy of the very same stylesheet. The cache key is now (resourceID, phase, xsltVersion, tracing) and the builders default to the shared SchematronProcessorFactory.getDefault (). To keep that safe, a Processor that is not the shared default now counts as a custom hook - like a custom URIResolver or ErrorListener - and bypasses the cache, unless forceCacheResult (true) is set. Otherwise a caller with its own Processor (e.g. carrying extension functions) could receive an XsltExecutable that was compiled with a different configuration.
  • Updated to ph-commons 12.5.0 (released 2026-09-21), which continues the "security by default" work in ph-xml and changes the XML processing defaults of all XSLT based engines (ph-schematron-isosch, ph-schematron-xslt, ph-schematron-schxslt, ph-schematron-schxslt2). The bundled ISO Schematron, SchXslt 1.x and SchXslt 2.x stylesheet chains are unaffected - they use neither extension functions nor xsl:result-document, and all their includes are class path based - so a setup that validates with local Schematron files keeps working unchanged. ph-schematron-pure-xpath and ph-schematron-pure-xslt are not affected by the ph-commons change itself, because they do not use SchematronTransformerFactory - they are secured separately, see the entry above.
    • Security fix: remote resource resolution in XSLT processing is now really denied, and no longer only logged. The v10.0.1 entry below claimed that a blocked resolution "returns null, so Saxon applies its own (restricted) default resolution" - that was wrong. Saxon treats null as "not handled" and then opened the URI itself, so document(), xsl:import and xsl:include still reached an attacker chosen host (SSRF), despite the warning in the log. ph-commons' DefaultTransformURIResolver now resolves a blocked resource to an empty document instead, which cannot be bypassed: document() evaluates to an empty node set, and an xsl:import/xsl:include fails to compile with XTSE0165. unparsed-text() is blocked as well, but reports the obscure Resolver for unparsed-text() returned non-StreamSource, because Saxon requires a StreamSource for a non-XML resource and the blocked resource is a DOM document. To allow specific remote schemes again, hand in a URIResolver that permits them - ...builder (...).uriResolver (new DefaultTransformURIResolver ().setAllowedRemoteSchemes ("http", "https")). Note that a resolver which returns null for a remote href no longer results in the remote resource being fetched by a fallback resolver - a resolver now has to resolve what it wants to allow.
    • Bugfix: SchematronProviderXSLTPrebuild, and therefore SchematronResourceXSLT, no longer wraps the caller supplied URIResolver in another DefaultTransformURIResolver. With the changed ph-commons semantics that wrapper ended the resolution of a remote href itself, so it silently overrode a caller supplied resolver that deliberately allowed a remote scheme. The wrapper was redundant anyway - the builder of SchematronXSLTConfig already defaults to a DefaultTransformURIResolver with the resource's parent directory as base URL, exactly like the other XSLT based engines.
    • Incompatible change: FEATURE_SECURE_PROCESSING is now enabled on every TransformerFactory created by SchematronTransformerFactory, because XMLFactory.defaultCustomizeTransformerFactory (TransformerFactory) of ph-commons applies it. Saxon implements secure processing by setting FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS to false, so in a custom Schematron or XSLT file:
      • xsl:result-document with an href no longer compiles - the error is XTSE0010 ("xsl:result-document is disabled when extension functions are disabled").
      • system-property() with a name in no namespace returns the empty string instead of the JVM system property. A prefixed name like system-property('xsl:vendor') is unaffected, and so are the system-property('xsl:product-name') calls of the SchXslt 1.x stylesheets.
      • environment-variable(), available-environment-variables() and available-system-properties() return the empty sequence.
      • a reflexive Java call (a java: namespace URI) is rejected with "External function calls have been disabled". This only matters with Saxon-PE/EE, because Saxon-HE - the edition this project depends on - never supported reflexive calls in the first place.
      • extension functions that are registered programmatically on the Saxon Processor, e.g. from ...builder (...).transformerFactoryCustomizer (...), keep working: Saxon considers an ExtensionFunctionDefinition trusted and allows calls to it even with external functions disabled. To opt out completely, set aTF.setAttribute (FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS, Boolean.TRUE) from a transformerFactoryCustomizer or from the global SchematronTransformerFactory.setTransformerFactoryCustomizer (Consumer) - the customizer runs after the ph-commons defaults are applied.
    • DefaultEntityResolver - the default entity resolver of AbstractSchematronResource for reading the Schematron resource - now blocks remote URL schemes as well and resolves such an entity to an empty document. Class path and file based entities are unaffected. Note that the default parser settings disallow DOCTYPE declarations anyway, so the resolver is not reached for an external entity under the default settings.
    • RelaxNGCompactSchemaCache.getValidator (...), as used by SchematronValidator, now hands the LSResourceResolver of its SchemaFactory to the created Validator, so that the RELAX NG validation of a Schematron file resolves external resources through the blocking SimpleLSResourceResolver as well. ph-commons additionally tries to deny the external DTD and schema access of that Validator, which JING does not support - this logs the two harmless warnings "Validator does not recognize property 'ACCESS_EXTERNAL_DTD'" and "... 'ACCESS_EXTERNAL_SCHEMA'" per created validator.
  • New tests for all the XML processing defaults described above: SchematronSecureProcessingTest in ph-schematron-xslt, SchematronRemoteAccessTest in ph-schematron-it, SchematronProcessorFactoryTest and SaxonSecureResourceResolverTest in ph-schematron-api plus SchematronTransformerFactoryTest.testSecureProcessingEnabledByDefault.
  • Reviewed the @Immutable / @ThreadSafe / @NotThreadSafe annotations of all classes of all modules, and corrected them where they did not match the actual thread safety of the class.
    • Bugfix: SchematronTransformerFactory and SchematronProcessorFactory were declared @Immutable, although both hold mutable static state. Their customizer field (setTransformerFactoryCustomizer (Consumer) resp. setProcessorCustomizer (Consumer)) was neither volatile nor guarded, so a customizer set from one thread was not guaranteed to be visible to another one. Both fields are now guarded by a SimpleReadWriteLock - like the error level determinator of SVRLHelper - and both classes are now declared @ThreadSafe.
    • SVRLLocationBeautifierRegistry was declared @NotThreadSafe, although it holds no mutable state at all. It is now declared @Immutable.
    • Added the missing annotation to 51 classes that carried none. No behaviour changed for these - they only document what the classes already did. Noteworthy for callers: the Schematron model readers and writers (PSReader, PSWriter, PSWriterSettings), the collecting and logging error handlers, XPathConfigBuilder and DefaultSchematronIncludeResolver are @NotThreadSafe and must not be shared between threads, whereas PSBoundSchemaCache is @ThreadSafe.

v10.0.2 - 2026-09-07

  • (pure) Foreign (non-Schematron) elements are now reported as warnings - fixes #186. The pure XPath engine (SchematronResourcePureXPath) can only evaluate Schematron elements and XPath expressions, so elements from other namespaces - typically XSLT ones like <xsl:function> or <xsl:variable> - were silently ignored so far. Now one warning per foreign element is sent to the configured IPSErrorHandler when the schema is bound, naming the element and its namespace URI. Use SchematronResourcePureXslt or one of the XSLT based engines if the XSLT elements need to be evaluated. The new helper PSForeignElementVisitor.forEachForeignElement (PSSchema, BiConsumer) in ph-schematron-model performs the underlying model walk and is engine independent.
  • New SaxonDOMSource to opt into Saxon's native tree model for DOM based validation - see #198. When an XML instance is passed to an XSLT based engine as a javax.xml.transform.dom.DOMSource, Saxon wraps the DOM and evaluates every XPath step against that wrapper. For large documents - in particular ones with long sibling lists - this repeated navigation dominates the validation runtime. The new class com.helger.schematron.saxon.SaxonDOMSource in ph-schematron-api is a DOMSource subclass that acts as a marker: pass it instead of a plain DOMSource to applySchematronValidation (Source), applySchematronValidationToSVRL (Source) or getSchematronValidity (Source) and the engine builds the transformer's configured native Saxon tree once per validation, instead of navigating the wrapped DOM. The Node based overloads always create a plain DOMSource, so one of the Source based methods must be used to opt in. All XSLT based engines honour it - SchematronResourceXSLT, SchematronResourceSCH, SchematronResourceSchXslt_XSLT2 and SchematronResourceSchXslt2 - but only if the wrapped node is a Document and the running transformer is a Saxon one. In every other case it behaves exactly like a regular DOMSource. The original DOM is never modified and the converted tree is not cached, so a single SaxonDOMSource can be reused across validations and across engines with different Saxon configurations, and later modifications of the DOM are picked up by the next validation. Because the transformation runs on a snapshot, two semantics differ from a regular DOMSource: DOM object identity is not retained, and base URIs are resolved against the source's system ID while the tree is built. Stay with a regular DOMSource if the stylesheet or an extension function requires the original DOM nodes or the wrapped DOM behaviour. This is purely opt-in - existing code keeps the previous wrapped DOM behaviour.

v10.0.1 - 2026-08-17

  • XInclude processing is now disabled by default in all XSLT-based engines (ph-schematron-isosch, ph-schematron-xslt, ph-schematron-schxslt, ph-schematron-schxslt2). It was unconditionally enabled since #86 and allowed a Schematron file to pull in arbitrary local and remote resources. The new switch SchematronTransformerFactory.setAllowXInclude (boolean) (default false, see SchematronTransformerFactory.DEFAULT_ALLOW_XINCLUDE) re-enables it. Because the created TransformerFactory objects and the XSLT templates derived from them are cached internally, the switch must be set before the first Schematron file is processed.
  • Updated to SchXslt2 1.11.2 (from 1.11.1) - affects ph-schematron-schxslt2 only. The upstream release (2026-07-20) fixes the placement of some attributes in the SVRL report and adds a RELAX NG grammar for the generated SVRL:
    • xml:* attributes are no longer copied wholesale into the report. svrl:text now only receives @xml:lang and @xml:space (in addition to @see, @icon and @fpi), and svrl:dir, svrl:emph and svrl:span no longer copy any xml:* attribute at all. Code that evaluated e.g. xml:base or xml:id on these SVRL elements no longer finds them.
    • The schxslt2 JAR now also contains the RELAX NG compact grammars content/svrl.rnc and content/svrl-schxslt.rnc. According to their headers they aim to be equivalent to the official SVRL grammar published in the 2025 edition of ISO Schematron, section D3.
  • Remote resource resolution in XSLT processing is denied by default. This is not a change of this release but a consequence of the ph-commons version in use (since ph-commons 12.3.2, so it is effective since ph-schematron v10.0.0) that was so far undocumented: the shared Saxon TransformerFactory of SchematronTransformerFactory.getDefault () uses ph-commons' DefaultTransformURIResolver, which resolves no remote URL scheme at all, to prevent Server Side Request Forgery (SSRF) via XSLT document(), xsl:import or xsl:include. Class path and file based resolution is unaffected, so setups where all XSLT documents reside locally keep working unchanged. A blocked resolution is logged as a warning and returns null, so Saxon applies its own (restricted) default resolution. To allow specific remote schemes again, pass an own resolver to SchematronTransformerFactory.createTransformerFactory (ErrorListener, URIResolver), e.g. new DefaultTransformURIResolver ().setAllowedRemoteSchemes ("http", "https").

v10.0.0 - 2026-07-16

  • New submodule ph-schematron-isosch — the SCH → XSLT preprocessing half of ph-schematron-xslt (the ISO Schematron stylesheet chain) was extracted into its own artifact. ph-schematron-xslt keeps only the "apply a pre-built XSLT to an XML instance" side. See the Migrations page for the moved classes, package locations and the Maven dependency to add.
  • New submodule ph-schematron-model — the engine-agnostic data model, the SCH XML reader/writer, the error handler types and the preprocessor were extracted from ph-schematron-pure into a much smaller artifact (no Saxon, no XPath evaluation). Consumers that only parse / serialize / preprocess Schematron schemas can now depend on ph-schematron-model directly. Packages were renamed to drop the .pure. segment — see the Migrations page for the rename table and a sed snippet to migrate imports.
  • New submodule ph-schematron-pure-xslt — pure-Java engine that generates an XSLT 3.0 stylesheet from the parsed PSSchema (no external ISO Schematron stylesheet chain) and runs it through Saxon s9api. Entry point SchematronResourcePureXslt accepts URIResolver, ErrorListener and a configurable XSLT version, supports <xsl:*> pass-through (xsl:function, xsl:key, xsl:include, ...) from foreign children of <sch:schema> and <xsl:choose>-bodied <sch:let>. Also ships SchematronToXsltConverter — a stand-alone SCH → XSLT tool that emits to a W3C DOM Document / String / OutputStream / Writer / File. The underlying PureXsltStylesheetGenerator.generate(...) returns a DOM Document so the runtime can hand it straight to Saxon XsltCompiler.compile(new DOMSource(...)) without a serialize-then-parse round trip. The engine's classes follow the PureXslt* naming convention (PureXsltStylesheetGenerator, PureXsltQueryBindingTransform, PureXsltTelemetry, EPureXsltVersion) so they don't clash with the ISO-XSLT engine's similarly-named types. Both wired into the Maven plugin (schematronProcessingEngine="pure-xslt") and the Ant task.
  • ph-schematron-pure-xslt version-conformant output (XSLT 1.0 / 2.0 / 3.0). EPureXsltVersion now covers XSLT 1.0, 2.0 and 3.0 (default 3.0), selectable via SchematronResourcePureXslt.builder().xsltVersion(...), SchematronPureXsltConfig and SchematronToXsltConverter.setXsltVersion(...). The generated stylesheet is now kept conformant to the chosen version — previously the SVRL @location was always populated with the XPath 3.0 fn:path(), so a stylesheet targeting 2.0 or 1.0 would not compile on a strict processor. The @location computation is now version-appropriate: fn:path() on 3.0, a generated phsch:path xsl:function on 2.0, and recursive phsch-path mode templates (no xsl:function) on 1.0. The XSLT 1.0 output was verified end-to-end against the JDK-bundled Xalan (XSLTC), i.e. a genuine XSLT 1.0 processor, for both non-namespaced and namespaced instances. EPureXsltVersion also gained isLT(...) / isLE(...) version-ordering helpers. Caveat: the built-in engine executes on Saxon-HE 12, which is an XSLT 3.0 processor — selecting 1.0 / 2.0 there only activates XSLT backwards-compatibility mode, not a true down-level engine. Whether a schema actually runs under a lower version additionally depends on the XPath used in its own test / context expressions (passed through verbatim); a queryBinding="xslt2" schema using e.g. matches() still needs a 2.0+ XPath at runtime regardless of the emitted @version.
  • Engine naming consolidated. SchematronResourcePure is now a deprecated source-compatible alias of SchematronResourcePureXPath (the canonical name; covariant setters keep chained code compiling). ESchematronMode is merged into ESchematronEngine — the latter now also covers the previous Mode-only XSLT_PREBUILT value and accepts every pre-v10 id (schematron, sch, schxslt-xslt2, pure-saxon, ...) as aliases. The Maven-plugin schematronProcessingEngine and Ant-task schematronProcessingEngine continue to accept the old strings unchanged.
  • SchematronValidationMojo now supports schxslt2. The Maven plugin's validate goal previously only handled schxslt (v1); selecting schematronProcessingEngine="schxslt2" now resolves to SchematronResourceSchXslt2 (SchXslt v2 / XSLT 3). The phaseName, languageCode and parameters parameters apply.
  • Ant task now supports schxslt2. The Ant Schematron task gained a SCHXSLT2 engine branch resolving to SchematronResourceSchXslt2 (SchXslt v2 / XSLT 3), so it now handles all six engine values (pure-xpath, pure-xslt, iso-schematron, schxslt, schxslt2, xslt) just like the validate goal. The ph-schematron-schxslt2 dependency was added to ph-schematron-ant-task.
  • ph-telemetry integration. Both SchematronResourcePureXPath and SchematronResourcePureXslt expose setTelemetry(boolean) and setPerAssertionTelemetry(boolean). When enabled, the engines emit OpenTelemetry-shaped spans (schematron.validate, schematron.parse, schematron.preprocess, schematron.generate, schematron.compile, schematron.execute, optional schematron.assertion), counters (schematron.assertions.failed, schematron.reports.fired, schematron.rules.fired, schematron.patterns.active) and a schematron.validate.duration histogram. Zero runtime cost when no ITelemetryTracerSPI / ITelemetryMeterSPI is registered — ph-telemetry degrades to no-op silently. Maven coordinates of ph-telemetry moved from com.helger.commons:ph-telemetry (formerly bundled with ph-commons) to the standalone repository com.helger.telemetry:ph-telemetry (current version 1.0.0-SNAPSHOT). The Java package com.helger.telemetry is unchanged, so source-level imports continue to work; only pom.xml <dependency> entries that referenced the artifact directly need their groupId updated.
  • New ph-schematron-benchmarks submodule runs the previously-test-only Main benchmarks as JMH suites.
  • Schematron edition awareness in the model (ph-schematron-model). The PS-model is now aware of which ISO/IEC 19757-3 edition each schema is targeting and can read/write/validate the full grammar of all four published editions (2006 / 2016 / 2020 / 2025). Specifically:
    • ESchematronVersion gains SCHEMATRON_2025 (with LATEST repointed at it), an getEditionYear() accessor returning the four-digit year string used by the new schematronEdition attribute, a getFromEditionYearOrNull(String) look-up, and an isOlderThan(ESchematronVersion) ordering helper.
    • PS-model elements completed. New top-level classes PSGroup, PSLibrary, PSRules, PSProperties, PSProperty. New base AbstractPSPatternLike extracted so that PSPattern and PSGroup are siblings (an instanceof PSPattern no longer matches a PSGroup). New attributes: let@as (2025), phase@from/@when (2025), rule@visit-each/@severity (2025), assert@severity and report@severity (2025), direct pattern@role/group@role (2025), pattern@documents/group@documents (2016), extends@href (2016), diagnostic@role (2020 RNC), schema-level <extends> / <param> / <rules> / <group> (2025), assert@properties/report@properties IDREFS (2016), and schema/library <properties> containers (2016). flag is now a list-of-tokens datatype (v4) — IPSHasFlag exposes getAllFlags()/addFlag(...) and setFlag(String) splits on whitespace. dir/emph/span accept the v4 dynamic group (<value-of> and <name>).
    • PSSchema.getSchematronEdition() / setSchematronEdition(...) carry the new 2025 schematronEdition attribute (typed ESchematronVersion). Reading an unrecognised attribute value emits a warning through the registered error handler.
    • Library root. PSReader.readLibrary() and readLibraryFromXML(IMicroElement) parse a <library> root document (the 2025 alternative to <schema>); PSLibrary.getAsMicroElement() writes it back.
    • Central PSVersionChecker. Walks a PSSchema and emits warnings for every feature whose introducing edition is newer than the schema's declared schematronEdition (or whenever no edition is declared). All warnings route through one private _warnFeatureUnavailable(...) method so the wording, severity or channel can be changed in a single place. The check is invoked automatically at the end of PSReader.readSchemaFromXML(...) and at the start of every PSWriter.write*(...) / getXMLString(...) call (the latter using an internal LoggingPSErrorHandler by default, overridable via PSWriter.setErrorHandler(...)). queryBinding values (xslt2, xslt3, xpath3, xpath31, xquery3, xquery31, xslt4, xpath4, xquery4, ...) are mapped to their introducing edition and flagged whenever an explicit older edition is declared.
    • Validation tightened to v4 RNC three-branch shape on PSPattern / PSGroup: is-a forbids <rule> and <let>, concrete (non-abstract / non-is-a) forbids <param>, abstract now allows <param> and <let> (newly permitted in 2025 — pre-2025 schemas using them are flagged through PSVersionChecker, not as a hard error).
  • Other v10 changes (engine renames pure → pure-xpath, module folder renames, ESchematronMode deprecation details, etc.) are documented in the Migrations page.

v9.2.0 - 2026-05-31

  • Updated to SchXslt2 v1.10.3
  • ph-schematron-pure migrated from JAXP XPath to the Saxon s9api. Schematron expressions are now compiled and evaluated as XPath 3.1 (default; configurable via the new EXPathVersion enum — supports 1.0 / 2.0 / 3.0 / 3.1 / 4.0). User-facing entry points (SchematronResourcePure, IPSBoundSchema, IPSValidationHandler, IPSErrorHandler) keep their DOM Node / NodeList signatures; the breaking changes are concentrated in IXPathConfig / XPathConfig / XPathConfigBuilder and in the bound-schema internals. See the Migrations wiki page.
  • (pure) XPathConfigBuilder replaces setXPathFactory(...) / setXPathFactoryClass(...) / setGlobalXPathFactory(...) / setXPathVariableResolver(...) / setXPathFunctionResolver(...) with setProcessor(Processor), setXPathVersion(EXPathVersion), addExtensionFunction(ExtensionFunction) / addAllExtensionFunctions(...), and addExternalVariable(QName, XdmValue) / addAllExternalVariables(...).
  • (pure) Custom function resolvers based on javax.xml.xpath.XPathFunction / MapBasedXPathFunctionResolver (from ph-commons) are no longer wired into the engine. Migrate by implementing net.sf.saxon.s9api.ExtensionFunction; arguments now arrive as XdmValue[] instead of List<Object> of DOM Nodes.
  • (pure) XQueryAsXPathFunctionConverter.loadXQuery(InputStream) returns ICommonsList<ExtensionFunction> instead of MapBasedXPathFunctionResolver. Feed the returned list into XPathConfigBuilder.addAllExtensionFunctions(...). The wrapper around an XQuery UserFunction now exposes the actual declared argument and result SequenceTypes, so Saxon applies the same automatic coercion (atomization, cardinality checks, type promotion) that it would for a built-in function — this fixes the long-standing DOMNodeWrapper cannot be cast to AtomicValue error when calling e.g. functx:are-distinct-values(...).
  • (pure) XPathFunctionFromUserFunction now implements ExtensionFunction (Saxon-typed throughout).
  • (pure) XPathEvaluationHelper is reshaped around XPathExecutable / XdmItem / XdmValue. Variants such as evaluateAsNodeList(...) returning DOM NodeList are gone; use evaluateAsXdmNodes(...) or evaluate(...).
  • (pure) XPathLetVariableResolver no longer implements javax.xml.xpath.XPathVariableResolver. It is now a per-thread QName → XdmValue store used internally during validation.
  • (pure) New XPathEvaluationContext (thread-local) is published as part of the SPI. The bound schema installs one per validate(...) call; the SVRL handler uses it to re-wrap DOM nodes against the same Saxon document wrapper and to read the currently-effective <let> variable bindings.
  • (pure) The bound-schema and bound-element types (PSXPathBoundSchema, PSXPathBoundRule, PSXPathBoundAssertReport, PSXPathBoundElement, PSXPathVariables, IPSXPathVariables) now hold Saxon XPathExecutable instances instead of javax.xml.xpath.XPathExpression.
  • Behavioural consequence (XPath 1.0 → 3.1). Expressions that were silently re-interpreted under XPath 1.0 (single-item conversion, implicit string coercion, eq/ne being syntax errors, etc.) now behave per the XPath 3.1 spec. Schematrons that target XPath 2.0/3.x in their queryBinding attribute work as written; Schematrons relying on XPath 1.0 quirks may need adjustment.
  • (pure) Saxon TinyTree input path. SchematronResourcePure.getAsNode(...) now parses the input XML straight into a Saxon TinyTree (via Processor.newDocumentBuilder()) and presents it as a DOM facade (net.sf.saxon.dom.NodeOverNodeInfo / DocumentOverNodeInfo). Every overload of applySchematronValidationToSVRL, getSchematronValidity and applySchematronValidation that takes a Source or IHasInputStream goes through this path. The internal validation loop short-circuits the DOM-bridge (DocumentWrapper.wrap) when it sees a Saxon-backed facade, so XPath evaluation runs directly against the TinyTree. Measured on JDK 21 against a ~1 MiB document with a representative schema, parse-only is ~28 % slower (Saxon's parser vs Xerces), validate-only is ~8 % faster, total wall-time is ~6 % faster. The win grows for more XPath-heavy schemas / repeated validations of the same document. See MainBenchmarkDomVsTinyTree in ph-schematron-pure/src/test/java. Caveats: when a custom EntityResolver is configured the TinyTree path is bypassed (so the resolver is honoured); the returned DOM facade is read-only (mutating operations throw NOT_SUPPORTED_ERR); overloads taking a pre-built org.w3c.dom.Node keep their existing behaviour.
  • (pure) <let> element bodies are now read instead of being silently discarded — fixes #189. The body text content is taken as a plain XPath expression when the value attribute is absent. XSLT-style bodies (<xsl:for-each>, <xsl:value-of> ...) still cannot be evaluated by the pure engine, but instead of the cryptic <let> has no 'value' failure the user now gets a clear, actionable warning naming the offending child element. Full XSLT-body support is tracked for a future, separate Saxon-XPath-and-XSLT engine module.

v9.1.1 - 2025-12-10

  • Added new enumeration ESchematronEngine to list the supported engines
  • Added new submodule ph-schematron-schxslt2 that deals with SchXslt2 (requiring XSLT 3.0 engine)
  • The Maven Plugin goal convert can now handle different Schematron engines via the new schematronEngine parameter

v9.1.0 - 2025-11-16

  • Updated to Saxon 12.9
  • Updated to ph-commons 12.1.0
  • Using JSpecify annotations

v9.0.1 - 2025-09-04

  • The XSLT based transformations use the Source based source as the basis. See #192 - thx @Lukvargen

v9.0.0 - 2025-08-25

  • Requires Java 17 as the minimum version
  • Updated to ph-commons 12.0.0
  • Updated to Saxon 12.8

v8.0.6 - 2025-03-10

  • Updated SVRL XML Schema to support specific SchXslt elements (metadata, suppressed-rule, message-code and message-category)

v8.0.5 - 2024-12-18

  • (pure) Added method PSReader.setSchematronIncludeResolver to define a custom Schematron include resolver

v8.0.4 - 2024-12-05

  • Updated to SchXslt 1.10.1
  • The XSLT binding will emit the attribute documents for SVRL element active-pattern parallel to the previous document to ensure ISO 2016 compliance.
  • (pure) Fixed a concurrency issue with XPath variables in multi-threaded environments. See #182 and #183 - thx @bertrand-lorentz

v8.0.3 - 2024-08-23

  • Provided the possibility to customize the created TransformerFactory used for XSLT transformations. See #176 - thx @SvenHaul
  • (pure) Fixed variable evaluation order. See #177 and PR #178 - thx @bertrand-lorentz

v8.0.2 - 2024-07-29

  • Updated to SchXslt 1.10
  • (pure) Improved the type auto detection for "for loop expressions". See #173 - thx @bertrand-lorentz
  • (pure) Improved the type auto detection for "filter expressions". See #175 - thx @bertrand-lorentz

v8.0.1 - 2024-07-17

  • Updated to Saxon 12.5
  • The default SVRL to error level mapper changed caution from WARNING to INFORMATION according to official sources. See #168 - thx @costas80
  • Improved the error handling in "pure" mode, in case a test expression does not evaluate properly. See #171 - thx @gediminasre
  • Improved the type detection of test expressions in "pure" mode if Saxon is used as the XSLT engine. See #170 - thx @bertrand-lorentz

v8.0.0 - 2024-03-09

  • Updated to Saxon 12.4
  • Updated to ph-commons 11.1.4
  • Updated to jvnet JAXB Maven Plugin
  • The variable resolution problem in pure mode was resolved by a kind contribution in #164 - thx @bertrand-lorentz
  • All deprecated elements, marked as "forRemoval" were removed
  • Renamed interface ISchematronXSLTValidator to ISchematronOutputValidator
  • Renamed method ISchematronXSLTValidator.getSchematronValidity to ISchematronOutputValidator.getSchematronOutputValidity
  • Renamed all implementing classes SchematronXSLTValidator* to SchematronOutputValidityDeterminator*
  • Renamed methods ISchematronXSLTBasedResource.(get|set)XSLTValidator to (get|set)OutputValidityDeterminator
  • Added new SchematronDebug debug log methods
  • Removed the experimental parallel validation in PSXPathBoundSchema
  • Removed the constructors of SchematronResourcePure with a boolean bLenient parameter. Use the version without it and call setLenient(boolean) instead
  • Honoring the @subject attribute in the Pure implementation. See #133 - thx @ericlop

v7.1.3 - 2023-12-06

  • Updated to Saxon 11.6
  • Added a new parameter ignoreWarnings to the Maven plugin validate goal. See #159 - thx @IacopoArduini-gmail

v7.1.2 - 2023-07-31

  • Updated to ph-commons 11.1

v7.1.1 - 2023-07-03

  • Updated to Saxon 11.5 - reverted back from Saxon 12.x as the default branch
  • DefaultSVRLErrorLevelDeterminator now also deals with caution as a warning level
  • Improved logging and code documentation

v7.1.0 - 2023-02-21

  • Updated to Saxon HE 12.0 - there seem to be backward incompatible changes
  • Updated to ANT 1.10.13
  • Updated to SchXslt 1.9.5
  • Fixed an error with pattern local variable resolution in "pure" implementation. See #142 - thx @bertrand-lorentz
  • Added a check in "pure" implementation, that Pattern ID must follow the XML NCName convention

v7.0.1 - 2023-02-22

  • Updated to ANT 1.10.13
  • Updated to SchXslt 1.9.5
  • Fixed an error with pattern local variable resolution in "pure" implementation. See #142 - thx @bertrand-lorentz
  • Added a check in "pure" implementation, that Pattern ID must follow the XML NCName convention

v7.0.0 - 2023-01-08

  • Using Java 11 as the baseline
  • Updated to ph-commons 11
  • Using JAXB 4.0 as the baseline
  • The creation of SVRL metadata was disabled in the SchXslt version (using parameter schxslt.compile.metadata with value false)
  • Removed deprecated methods

v6.3.4 - 2022-11-17

v6.3.3 - 2022-08-17

  • Updated to Saxon 11.4 (which updates XMLResolver to 4.4 which updates to Apache Http Client 5.x)
  • Extended ISchematronXSLTBasedResource API to enable/disable the SVRL validation

v6.3.2 - 2022-07-06

  • Updated the RelaxNG components to version 20220510
  • Updated to SchXslt 1.9.1
  • The Maven "Schematron to XSLT" plugin has the parameter "stopOnError" (boolean)

v6.3.1 - 2022-05-05

  • Updated to Saxon 11.3
  • Updated to SchXslt 1.8.7
  • Updated the RelaxNG components to version 20181222

v6.3.0 - 2022-03-08

  • Updated to Saxon 11.2
  • Updated to SchXslt 1.8.6

v6.2.8 - 2021-12-29

  • Updated to SchXslt 1.8.5
  • Deprecated SchematronHelper.applySchematron in favour of the native methods
  • The Maven plugin prints a progress report, if the execution takes longer than 5 seconds. This can be enabled/disabled with the new parameter showProgress.
  • The diagnostic-reference elements in the Pure implementation now copy the rich attributes. See #126 - thanks @costas80

v6.2.7 - 2021-12-09

  • Fixed an error in the ISO Schematron, that caused a message to be emitted if a iso:name[@path] exists that does not had a @select attribute.
  • Avoid double evaluation of Schematron when using the XSLT based approach with disabled caching

v6.2.6 - 2021-11-24

  • Extended SchematronResourceSCH API. See #122 - thanks @cshjsc
  • Made an improvement in the ISO Schematron XSLT on attribute handling. See #123 - thanks @Falcon2677

v6.2.5 - 2021-11-22

  • Updated to ph-commons 10.1.5
  • Fixed quite some SonarCloud issues - nothing critical
  • Fixed the automatic resource ID of in-memory resources. See #118 - thanks @olavivaino and @qligier
  • Added new factory method overloads for class SchematronResourceSchXslt_XSLT2

v6.2.4 - 2021-11-02

  • Updated to ANT 1.10.12
  • Added a new unchecked exception class SchematronInterruptedException that is thrown in case the current Thread gets interrupted during the compilation of XSLTs/Schematrons. See #119 - thanks @Michiel-s

v6.2.3 - 2021-10-14

  • Updated to SchXslt 1.8.4
  • Added some factory methods in the PS* classes

v6.2.2 - 2021-09-27

  • Updated to Saxon-HE 10.6
  • Updated to SchXslt 1.8.2
  • Added the JAXB dependencies to the ANT task and the Maven plugin
  • Added a new parameter xsltHeader to the sch2xslt Maven plugin goal, to add a comment to the generated XSLTs
  • Added a new parameter schHeader to the preprocessing Maven plugin goal, to add a comment to the generated SCHs

v6.2.1 - 2021-08-01

  • Updated to SchXslt 1.7.4
  • Updated the Maven plugin configuration on validation - xmlIncludes, xmlExcludes, xmlErrorIncludes and xmlErrorExcludes are now of type String[] compared to String[] before. No need to change pom.xml configurations afaik.

v6.2.0 - 2021-05-02

  • Updated to ph-commons 10.1
  • Updated to Saxon-HE 10.5
  • Updated to SchXslt 1.7.1

v6.1.0 - 2021-03-22

  • Updated to ph-commons 10
  • Extended API to have SchematronResource fromClassPath overloads with ClassLoader parameter
  • Made SVRLHelper.getBeautifiedLocation more flexible
  • The update to ph-commons 10 also fixed #110 - thanks @monapadu

v6.0.3 - 2021-01-28

  • Updated to SchXslt 1.6.2
  • Changed the internal XSLT caching lock management, so that the cache can be filled in parallel

v6.0.2 - 2021-01-06

  • Updated the svrl.xsd to match the requirements of diagnostic-reference of ISO Schematron (see #85) - thanks @nkutsche
  • Language tag of a diagnostic is preserved in SVRL output (see #82) - thanks @dmj

v6.0.1 - 2020-12-04

  • Made the "id" attribute accessible in AbstractSVRLMessage (see #103) - thanks @mangeg

v6.0.0 - 2020-11-24

  • Using the new Maven group ID com.helger.schematron (the group ID of the Maven plugin stays untouched: com.helger.maven)
  • Created a new submodule ph-schematron-api that contains the shared implementation parts
  • Created a new submodule ph-schematron-pure that contains the pure Java implementation
  • Created a new submodule ph-schematron-xslt that contains the XSLT based implementation (https://github.com/Schematron/schematron)
  • Created a new submodule ph-schematron-schxslt that contains the SchXslt based implementation (https://github.com/schxslt/schxslt)
  • Dropped the submodule ph-schematron - pick one of ph-schematron-xslt or ph-schematron-pure instead
  • Started a Wiki at https://github.com/phax/ph-schematron/wiki
  • Extended the SVRL XSD to also work if foreign elements are allowed see #111 (thanks @flowrider3000) and #101 (thanks @Michiel-s)
  • The default processing engine for the Schematron validation changed from pure to schematron
  • Added new class SchematronXSLTValidatorSuccessfulReportOnly in ph-schematron-xslt
  • The pure implementation now also registers handles for the query bindings xpath3 and xslt3
  • Added SchXslt 1.5.2 as a new way to create SVRL (the engine ID is 'schxslt-xslt2')
  • The ANT task and the Maven plugin also support the SchXslt engine

v5.6.5 - 2020-11-19

  • Updated to Saxon-HE 10.3
  • Added SchematronResourceXSLTCache.clearCache() and SchematronResourceSCHCache.clearCache() (see #109) - thanks @SnowMakerDemo

v5.6.4 - 2020-10-13

  • Updated to ANT 1.10.9
  • The Maven plugin now also complains on "successful reports" (see #108) - thanks @lueck

v5.6.3 - 2020-09-28

  • Extended SchematronResourceXSLT API (see PR #107) - thanks @jw3

v5.6.2 - 2020-09-17

  • Updated to Jakarta JAXB 2.3.3

v5.6.1 - 2020-08-30

  • Updated to ANT 1.10.8
  • Updated to Saxon-HE 10.2

v5.6.0 - 2020-03-29

  • Updated to ph-commons 9.4.0
  • Updated to Saxon-HE 10.0
  • Changed the internal error handler to use IError and therefore also improve the error handling quality

v5.5.0 - 2020-03-08

  • Updated to Saxon-HE 9.9.1-7
  • Improved XPath configuration for the pure Schematron implementation (see PR #98) - thanks @aanno
    • See new interface IXPathConfig and builder class XPathConfigBuilder

v5.4.1 - 2020-03-08

  • Updated to Saxon-HE 9.9.1-6
  • Added a "lenient" setting to the pure Schematron implementation to allow reading Schematrons with an old namespace URI (see PR #97) - thanks @aanno
  • Added the "lenient" flag to the Maven plugin for validating documents

v5.4.0 - 2019-11-29

  • Changed the package of the generated SVRL classes from org.oclc.purl.dsdl.svrl to com.helger.schematron.svrl.jaxb to avoid incompatibilities with other Schematron solutions (incompatible change)
  • Removed ph-sch2xslt-maven-plugin which was deprecated long time ago. Use ph-schematron-maven-plugin instead.

v5.3.0 - 2019-11-22

  • Updated to ANT 1.10.7
  • Updated to Saxon-HE 9.9.1-5
  • The default include handler of the pure version, now allows to include non-Schematron XMLs
  • The Maven plugin preprocess Mojo now emits the XML declaration as well
  • Added support for the Schematron query bindings xpath and xpath2 (issue #80)
  • Extended API of SCHTransformerCustomizer for forcing result caching (issue #87), added new parameters in Maven plugin and ANT task for this setting (forceCacheResult)
  • Renamed method IPSValidationHandler.onRule to onFiredRule (incompatible change)
  • Added methods IPSValidationHandler.onRuleStart and and for chaining

v5.2.0 - 2019-06-13

  • Fixed a ClassLoader issues for ANT task (issue #78)
  • Updated the SVRL XSD with the ISO Schematron 2016 changes (incompatible change)

v5.1.1 - 2019-06-12

  • Fixed regression in ANT task (see issue #71)

v5.1.0 - 2019-06-11

  • Updated to ANT 1.10.6
  • Updated to Saxon-HE 9.9.1-3
  • Fixed created Schematron XSDs (issue #76)
  • Improved logging results in ANT task (issue #71)
  • Added new ANT task parameters failOnValidationError, failOnValidationWarn and failOnValidationInfo that all default to false (issue #50)
  • Removed deprecated methods
  • SCH and XSLT based compilers now correctly pass the URIResolver to the created XSLT - important for include resolution (issue #77)
  • The default include resolver was improved for file based SCH and XSLT validation

v5.0.10 - 2019-05-07

  • Improved handling of XML elements from other namespaces (issue #51)
  • The Maven preprocess goal now uses the <ns> elements from the Schematron as XML namespace context
  • The ANT task is now build against 1.10.x but is source compatible with ANT 1.9.x
  • Added Java 12 support for Maven plugins

v5.0.9 - 2019-04-25

  • Updated to ant 1.9.14
  • Updated to Saxon-HE 9.9.1-1
  • The Maven plugin ph-sch2xslt-maven-plugin is now deprecated and will be removed in the next major version. The functionality was moved "as-is" to the ph-schematron-maven-plugin
  • The Maven plugin ph-schematron-maven-plugin got a new goal preprocess to create preprocessed Schematrons (issue #75)

v5.0.8 - 2018-11-26

  • Fixed an initialization error in the SCH to XSLT maven plugin in JDK 11

v5.0.7 - 2018-11-22

  • Updated to ph-commons 9.2.0
  • Updated to Saxon-HE 9.9.0-1

v5.0.6 - 2018-09-09

  • The Ant task has the possibility to provide custom parameters to XSLT and SCH validations (issue #62)
  • Instances of SchematronResourceSCH now have a default URI resolver to resolve references relative to the source Schematron
  • Requires ph-commons 9.1.5
  • SVRLHelper can now handle null inputs

v5.0.5 - 2018-08-13

  • Updated to Saxon-HE 9.8.0-14
  • Added support to disable "fail fast" mode in ph-schematron-maven-plugin (see issue #69)
  • Fixed custom error handling for report when using role (see issue #66 again)

v5.0.4 - 2018-05-14

  • Really fixed OSGI ServiceProvider configuration
  • Updated to Saxon-HE 9.8.0-12

v5.0.3 - 2018-05-09

  • Fixed OSGI ServiceProvider configuration

v5.0.2 - 2018-04-12

  • Added new interface ISchematronXSLTBasedResource as a common base class for XSLT based validations
  • Improved the DefaultSVRLErrorLevelDeterminator implementation to be more flexible and cater for more error levels
  • Updated to Saxon-HE 9.8.0-11
  • The Maven plugins now require Maven 3.0
  • Added new parameter parameters to the ph-sch2xslt-maven-plugin
  • Finally the role attribute is copied to a failed assertion when using the pure implementation
  • The Ant task has the possibility to provide values for role and flag that are interpreted as error (issue #66)

v5.0.1 - 2018-02-01

  • Moved getBeautifiedLocation to class SVRLHelper and made it public
  • Updated to Saxon-HE 9.8.0-7
  • Requires ph-commons 9.0.1

v5.0.0 - 2018-01-02

  • Updated to ph-commons 9.0.0
  • Added new ANT task for preprocessing Schematron files only
  • Improved support for base-uri() XPath function when using the pure implementation (issue #47)
  • Fixed issue with role attribute in SVRL when using pure implementation (issue #54)
  • Updated to Saxon-HE 9.8.0-6 - therefore no XLST v1 scripts can be used anymore - this only works up to 9.7.x!
  • Added ANT task property failOnError (issue #57)

v4.3.4 - 2017-07-27

  • Added new class SchematronDebug that centrally manages the debug flags for logging etc.

v4.3.3 - 2017-07-27

  • Reverted to Saxon-HE 9.7.0_18 because of incompatibilities in production

v4.3.2 - 2017-07-25

  • Updated to Saxon-HE 9.8.0-3
  • Changed all XSLT scripts to use and create only XSLT 2.0 (because Saxon 9.8.x does not support XSLT 1.0 anymore)
  • Updated to ph-commons 8.6.6

v4.3.1 - 2017-05-29

  • Updated to ph-commons 8.6.5
  • Fixed too verbose logging of created XSLT
  • Removed some old deprecated methods

v4.3.0 - 2017-05-15

  • Updated to Saxon-HE 9.7.0-18
  • Fixed an error with nested SVRL directories in Maven plugin (issue #37)
  • Added possibility to use "negative" tests in Maven plugin (issue #38)
  • Added ANT plugin to validate Schematron resources (issue #39, issue #40)
  • Using the EntityResolver also for the XML files to be validated (not just the Schematron)
  • Added a default EntityResolver and a default URIResolver that tries to resolve includes relative to the base Schematron.

v4.2.2 - 2017-02-22

  • Updated to Saxon-HE 9.7.0-15
  • Fixed usage of <let> in <extend>-based rules for the pure implementation (issue #36)

v4.2.1 - 2017-01-20

  • Added WrappedCollectingPSErrorHandler

v4.2.0 - 2017-01-09

  • Binds to ph-commons 8.6.0
  • Updated to Saxon-HE 9.7.0-14
  • Added a new Schematron validation Maven plugin

v4.1.1 - 2016-11-03

  • Added possibility to use XML EntityResolver (issue #30)
  • Updated to Saxon-HE 9.7.0-10

v4.1.0 - 2016-09-09

  • Binding to ph-commons 8.5.x

v4.0.2 - 2016-07-22

v4.0.1 - 2016-07-05

  • better integration of sch2xslt Maven plugin into m2e - thanks to @baerrach

v4.0.0 - 2016-06-15

  • updated to JDK8
  • updated to Saxon-HE 9.7

v3.0.1 - 2015-10-14

  • keep diagnostics in Pure version; resource resolving emits to error handler

v3.0.0 - 2015-07-29

  • because of update to ph-commons 6.0.0; extended XSLT based API

v2.9.2 - 2015-03-12

  • because of update to ph-commons 5.6.0

v2.9.1 - 2015-02-03

  • fixes a classloader issue added in 2.9.0

v2.9.0 - 2015-01-30

  • introduced new APIs in several places
  • updated to Saxon-HE 9.6

v2.8.4 - 2014-10-30

v2.8.3 - 2014-09-16

  • An easy way to use XQuery functions (like funcx library) as custom XPath functions was added

v2.8.2 - 2014-09-02

v2.8.1 - 2014-08-29

v2.8.0 - 2014-08-28

⚠️ **GitHub.com Fallback** ⚠️