Schematron
XSD validates structure (what elements appear where, what types
they hold). Schematron validates rules (when this attribute is X,
that other element must be Y; book years can’t be negative; an order
line’s qty × price must equal the order’s total). Use both
together when one alone isn’t enough.
Enabling
The Schematron compiler ships behind the xslt feature (Schematron
compiles to XSLT internally):
[dependencies]sup-xml = { version = "*", features = ["xslt"] }Compile and validate
use sup_xml::xslt::schematron::Schematron;
let sch = Schematron::compile_str(r#" <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> <sch:pattern id="book-rules"> <sch:rule context="book"> <sch:assert test="@isbn">every book must have an ISBN</sch:assert> <sch:assert test="@year >= 1450">books before 1450 predate movable type</sch:assert> <sch:report test="@year < 1900">marked as historical</sch:report> </sch:rule> </sch:pattern> </sch:schema>"#)?;
let report = sch.validate_str("<book isbn='978-…' year='1850'/>")?;for finding in &report.findings { println!("[{:?}] {} (at <{}>)", finding.kind, finding.message, finding.context_name);}Each Finding carries kind, message, the firing pattern_id /
assertion_id / role, a stable location_id for the offending node,
and its context_name (the node’s local-name).
Finding::kind distinguishes FailedAssert (an <assert> test failed)
from SuccessfulReport (a <report> test fired its diagnostic), so
consumers can route them to errors vs informational logs.
How it works
The Schematron schema is compiled to an XSLT stylesheet — this is exactly what the ISO Schematron reference implementation does — and run through SupXML’s XSLT engine. That means:
- XPath expressions in
<sch:assert>/<sch:report>get the full XPath 1.0 + 2.0 surface SupXML ships (axes, predicates, EXSLT functions, namespace handling, etc.). - Performance is whatever XSLT performance is — see the XSLT guide and the performance reference.
<sch:phase>,<sch:pattern>,<sch:let>, and<sch:include>all desugar to the corresponding XSLT constructs at compile time.
Combining with XSD
For documents that need both structural and rule validation, run XSD first (cheaper, errors more local) and Schematron second (catches what XSD can’t express):
use sup_xml::xsd::Schema;use sup_xml::xslt::schematron::Schematron;
let xsd = Schema::compile_str(xsd_source)?;let sch = Schematron::compile_str(sch_source)?;
xsd.validate_str(instance)?; // structurelet report = sch.validate_str(instance)?; // business rulesif !report.findings.is_empty() { eprintln!("{} schematron findings", report.findings.len());}Typical pattern in regulated domains (financial messaging, ePub, healthcare data exchange) — the XSD locks the schema shape, the Schematron enforces the policy.
From the shell
sup-xml validate --schematron rules.sch instance.xmlsup-xml validate --schema schema.xsd --schematron rules.sch instance.xmlThe CLI’s validate subcommand takes --schematron (and can
combine it with --schema for XSD + Schematron in one pass). Exit
code is 0 on no findings, 1 if any rule fired.