XSchematron.Cli 0.2.2

dotnet tool install --global XSchematron.Cli --version 0.2.2
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local XSchematron.Cli --version 0.2.2
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=XSchematron.Cli&version=0.2.2
                    
nuke :add-package XSchematron.Cli --version 0.2.2
                    

XSchematron.Cli

A cross-platform .NET toolxschematron — that validates an XML document against one or more ISO Schematron (.sch) rule files and exits non-zero when it finds problems. Built for build scripts and CI pipelines: one command, machine-readable output, a controllable exit code.

Schematron is the business-rule layer XSD can't express ("if the VAT category is X, the rate must be Y"), which is why it carries the substantive rules in e-invoicing standards such as CEN EN 16931 and Peppol BIS Billing 3.0.

Need to validate from inside your own code instead? Use the XSchematron library this tool is built on.

Install

# as a global tool
dotnet tool install -g XSchematron.Cli

# or pinned per repository, which is the CI-friendly form
dotnet new tool-manifest          # once per repo, if you have no .config/dotnet-tools.json
dotnet tool install XSchematron.Cli
dotnet tool restore               # on a fresh clone or CI runner

The command is xschematron (or dotnet xschematron when installed as a local tool). It runs on the .NET 8 or .NET 10 runtime.

You supply the rule files. This package ships code only — no .sch files are included. Point the tool at the rule sets you need (CEN EN 16931, Peppol BIS Billing 3.0, or your own).

Usage

# one or more rule files
xschematron validate invoice.xml -s CEN-EN16931-UBL.sch -s PEPPOL-EN16931-UBL.sch

# or a whole directory of them
xschematron validate invoice.xml --schema-dir ./schematron --recursive

# machine-readable output, written to a file
xschematron validate invoice.xml --schema-dir ./schematron --format json -o report.json
Usage:
  xschematron validate <xml-file> [options]

Options:
  -s, --schema <schema>           A Schematron (.sch) file to validate against. Repeat to use several.
  --schema-dir <schema-dir>       Directory of .sch files to validate against.
  --recursive                     Search --schema-dir recursively.
  --phase <phase>                 Run only the patterns activated by this phase id. Defaults to all patterns.
  --format <json|text>            Output format: text or json. [default: text]
  -o, --output <output>           Write the report to this file instead of stdout.
  --fail-on <error|info|warning>  Lowest severity that causes a non-zero exit code. [default: error]
  --quiet                         Print only the summary line, not individual messages.

--schema and --schema-dir combine; the resulting set is de-duplicated and validated in a stable order.

Exit codes

Code Meaning
0 Valid — no message reached the --fail-on threshold
1 One or more messages at or above --fail-on
2 Usage error, missing document, or a rule file that failed to load

The split matters in CI: 1 means your document is wrong, 2 means the check itself is misconfigured. Don't treat them as the same failure.

Output

text (default)

Messages grouped by the rule file that produced them, then a summary line. Paths are printed absolute, as resolved:

/work/schematron/CEN-EN16931-UBL.sch:
  error: [BR-CO-17] VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals.
    at /Invoice[1]/cac:TaxTotal[1]/cac:TaxSubtotal[1]
  error: [BR-S-09] The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where VAT category code (BT-118) is "Standard rated" shall equal the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119).
    at /Invoice[1]/cac:TaxTotal[1]/cac:TaxSubtotal[1]/cac:TaxCategory[1]

/work/invoice.xml: 2 error(s), 0 warning(s), 0 info.

Rule sets conventionally repeat the rule id at the head of the message text ("[BR-CO-17]-VAT category…"); the text format shows the id once and drops the duplicate. --quiet prints the summary line only.

json

A stable contract — an object with document, isValid, and a messages array. Null-valued properties are omitted, and message carries the rule text verbatim, id prefix included.

{
  "document": "/work/invoice.xml",
  "isValid": false,
  "messages": [
    {
      "schema": "/work/schematron/CEN-EN16931-UBL.sch",
      "ruleId": "BR-CO-17",
      "patternId": "UBL-model",
      "kind": "Assert",
      "severity": "Error",
      "flag": "fatal",
      "message": "[BR-CO-17]-VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals.",
      "xPathContext": "/Invoice[1]/cac:TaxTotal[1]/cac:TaxSubtotal[1]",
      "schemaLineNumber": 224,
      "documentLineNumber": 66,
      "documentLinePosition": 6
    }
  ]
}

schemaLineNumber locates the assert in the .sch file; documentLineNumber / documentLinePosition locate the offending node in the validated document.

Severity mapping

Source Severity
<assert> fails Error
<assert flag="fatal"> fails Error
<assert flag="warning"> fails Warning
<report> fires Info (does not make a document invalid)

Use --fail-on warning to make warnings break the build, or --fail-on info to fail on anything the rule sets report at all.

In a GitHub Actions workflow

- uses: actions/setup-dotnet@v4
  with:
    dotnet-version: "8.0.x"

- name: Install the validator
  run: dotnet tool install -g XSchematron.Cli

- name: Validate invoices
  run: |
    for invoice in samples/*.xml; do
      xschematron validate "$invoice" \
        --schema-dir ./schematron --recursive \
        --fail-on warning --format json -o "${invoice%.xml}.report.json"
    done

Notes and limits

  • Rule sets using queryBinding="xslt2" work without an XSLT 2.0 processor — the underlying engine walks the rules and evaluates XPath 2.0 directly, with no XSLT compilation step.
  • Custom <function> blocks are supported two ways: 19 national-identifier checksum and format functions are natively implemented, and any declaration whose body is pure XPath is evaluated as written. A .sch declaring a function that is neither fails to load with exit code 2 rather than silently skipping the rule.
  • document('…') resolves against the schema's own directory only — a rule file cannot read arbitrary paths or fetch over the network, which matters when the .sch came from elsewhere.
  • <sch:include> and abstract patterns (abstract="true" / is-a) are not supported, and a .sch using them fails to load with exit code 2 naming the construct. Use the flattened preprocessed/ variant upstream publishes alongside the modular source.
  • <extends> rule inheritance, SVRL output, and SARIF output are not supported. See the library README for the full feature table.

Licensing

MIT — see LICENSE. Third-party attribution (XPath2.Net is MS-PL, System.CommandLine is MIT) is in NOTICE.md, included in this package and in the repository.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.2.2 92 9/8/2026
0.2.1 99 9/7/2026
0.2.0 104 9/6/2026
0.1.1 106 9/6/2026
0.1.0 100 9/6/2026