Plugin gives Gradle-native tasks for XML transformations and validations with Saxon-backed execution. It supports XML, XIR, and canonical JSON routing, with one orthogonal DSL across task types.
Use this plugin when project needs repeatable XML processing in build pipeline:
- Transform XML with XSLT or XQuery
- Validate XML with XSD or Schematron
- Bootstrap Schematron from XSD
- Compile and run SEL/SHACL extraction flows
- Compile and run XSpec test suites
Main task types:
name.jurgenei.gradle.xml.XsltTaskname.jurgenei.gradle.xml.XQueryTaskname.jurgenei.gradle.xml.SchematronTaskname.jurgenei.gradle.xml.XsdTaskname.jurgenei.gradle.xml.SchematronBootstrapTaskname.jurgenei.gradle.xml.SchematronSelCompileTaskname.jurgenei.gradle.xml.SchematronSelExtractTaskname.jurgenei.gradle.xml.ShaclSelCompileTaskname.jurgenei.gradle.xml.ShaclSelExtractTaskname.jurgenei.gradle.xml.XSpecCompileTaskname.jurgenei.gradle.xml.XSpecTask
Plugin ID: name.jurgenei.gradle.xml
plugins {
id("name.jurgenei.gradle.xml")
}- Saxon HE execution for XSLT and XQuery
- Validation outputs normalized to SVRL (+ optional JUnit for validation/XSpec)
- File-tree mode and explicit single-file mode
- Parallel workers with virtual threads
- Parameter passing via
param(name, value) - XIR input/output support
- Canonical JSON input/output support
- Orthogonal output naming:
outputFormat(compact,beautified)outputMode(auto,native,canonical)
- Validation format naming unchanged:
format(...)/reportFormat(SVRL,JUNIT,SVRL_AND_JUNIT)
XIR is alternate syntax for XML/XDM structures, not new data model. Think: same information as XML, different representation optimized for compactness and readability in diffs and generated artifacts.
- Less punctuation noise vs XML
- Easier nested-structure scanning in code review
- Roundtrip-friendly with existing XML tooling path
- Works with same transformation and validation pipeline
| XIR token | Meaning | XML/XDM equivalent |
|---|---|---|
(...) |
node expression | element/document node structure |
{...} |
associative payload | attributes + namespace bindings, or XDM map value |
[...] |
sequence payload | XDM sequence / array value |
. |
document node head | XML document root wrapper |
? |
PI node head | processing instruction |
! |
comment node head | XML comment |
Disambiguation:
{...}denotes XDM map value.[...]denotes XDM sequence/array value.(map ...)and(array ...)are XML elements unless mapped from XPath Functions XML namespace bridge.- If first child of element is map value, explicit empty attribute block can disambiguate:
(element {} { key value })
XML and XIR side-by-side example
<?xml version="1.0" encoding="UTF-8"?>
<book id="b1" xmlns:m="urn:math">
<title>XML</title>
</book>(. { version "1.0" encoding "UTF-8" }
(book { id "b1" xmlns:m "urn:math" }
(title "XML")))Runtime note:
- XIR parser/serializer ships inside plugin artifact.
- No extra runtime dependency needed.
Transformations use XsltTask and XQueryTask.
- File-tree mode:
source(...)+outputDir - Explicit mode:
input(...)+output(...)
outputExtensionoutputMethod(xml,json,text)outputFormat(compact,beautified) for XIR/canonical JSON styleoutputMode(auto,native,canonical) for JSON routingworkersfailOnErrorparam(name, value)
Input/output example in XML and XIR
<book id="b1"><title>XML</title></book>(book {id "b1"} (title "XML"))Groovy Task Def (XSLT)
tasks.register('xmlToXir', name.jurgenei.gradle.xml.XsltTask) {
style 'src/main/xslt/identity.xsl'
source(fileTree('src/main/xml') { include '**/*.xml' })
outputDir.set(layout.buildDirectory.dir('out/xslt'))
outputExtension.set('.xir')
outputFormat.set('beautified')
outputMode('auto')
workers.set(4)
param 'tenant', 'acme'
}Kotlin Task Def (XSLT)
tasks.register<name.jurgenei.gradle.xml.XsltTask>("xmlToXir") {
style("src/main/xslt/identity.xsl")
source(fileTree("src/main/xml") { include("**/*.xml") })
outputDir.set(layout.buildDirectory.dir("out/xslt"))
outputExtension.set(".xir")
outputFormat.set("beautified")
outputMode("auto")
workers.set(4)
param("tenant", "acme")
}Input/output example in XML and XIR
<book id="b1"><title>XML</title></book>(book {id "b1"} (title "XML"))Groovy Task Def (XQuery)
tasks.register('runXQuery', name.jurgenei.gradle.xml.XQueryTask) {
query 'src/main/xquery/main.xq'
source 'src/main/xml/input.xml'
outputDir.set(layout.buildDirectory.dir('out/xquery'))
outputExtension.set('.json')
outputMode('canonical')
outputFormat.set('beautified')
}Kotlin Task Def (XQuery)
tasks.register<name.jurgenei.gradle.xml.XQueryTask>("runXQuery") {
query("src/main/xquery/main.xq")
source("src/main/xml/input.xml")
outputDir.set(layout.buildDirectory.dir("out/xquery"))
outputExtension.set(".json")
outputMode("canonical")
outputFormat.set("beautified")
}outputMode('auto'): parse JSON canonically on input; output tries canonical-first with native fallback.outputMode('native'): no canonical input parser; output still canonical-first with native fallback.outputMode('canonical'): canonical input and canonical output only.
Legacy aliases still work:
jsonMode/modexirFormat/xformat
SchematronSelCompileTask: compilesel:*metadata to extraction stylesheetSchematronSelExtractTask: run extraction and emit grouped outputsShaclSelCompileTask+ShaclSelExtractTask: same compile/extract shape for SHACL-driven flows
Validation tasks use shared contract:
outputExtension(default.svrl.xml)workers(default1)failOnError(defaulttrue)format(...)/reportFormatvalues:SVRLJUNITSVRL_AND_JUNIT
Engine options:
AUTO(default; chooses best available engine)SAXONJAXP
XML and XIR validation inputs
<root><wrong>bad</wrong></root>(root (wrong "bad"))Groovy Task Def (XSD validation)
tasks.register('validateXsd', name.jurgenei.gradle.xml.XsdTask) {
schema 'src/main/xsd/schema.xsd'
source(fileTree('src/main/xml') { include '**/*.xml' })
outputDir.set(layout.buildDirectory.dir('reports/xsd'))
format(name.jurgenei.gradle.xml.validation.ReportFormat.SVRL_AND_JUNIT)
engine.set(name.jurgenei.gradle.xml.validation.XsdEngine.AUTO)
failOnError.set(false)
}Kotlin Task Def (XSD validation)
tasks.register<name.jurgenei.gradle.xml.XsdTask>("validateXsd") {
schema("src/main/xsd/schema.xsd")
source(fileTree("src/main/xml") { include("**/*.xml") })
outputDir.set(layout.buildDirectory.dir("reports/xsd"))
format(name.jurgenei.gradle.xml.validation.ReportFormat.SVRL_AND_JUNIT)
engine.set(name.jurgenei.gradle.xml.validation.XsdEngine.AUTO)
failOnError.set(false)
}Schematron options include:
style(...)for persistent compiled stylesheet cache- optional
transpilerStylesheet(...) - SchXslt parameter set:
phase,severityThreshold,streamable,compactReport, and others
Groovy Task Def (Schematron validation)
tasks.register('validateSchematron', name.jurgenei.gradle.xml.SchematronTask) {
schema 'src/main/schematron/rules.sch'
style 'build/generated/schematron/rules.compiled.xsl'
source(fileTree('src/main/xml') { include '**/*.xml' })
outputDir.set(layout.buildDirectory.dir('reports/schematron'))
format(name.jurgenei.gradle.xml.validation.ReportFormat.SVRL_AND_JUNIT)
phase.set('#ALL')
severityThreshold.set('warning')
workers.set(4)
failOnError.set(false)
}Kotlin Task Def (Schematron validation)
tasks.register<name.jurgenei.gradle.xml.SchematronTask>("validateSchematron") {
schema("src/main/schematron/rules.sch")
style("build/generated/schematron/rules.compiled.xsl")
source(fileTree("src/main/xml") { include("**/*.xml") })
outputDir.set(layout.buildDirectory.dir("reports/schematron"))
format(name.jurgenei.gradle.xml.validation.ReportFormat.SVRL_AND_JUNIT)
phase.set("#ALL")
severityThreshold.set("warning")
workers.set(4)
failOnError.set(false)
}SchematronBootstrapTask can generate initial Schematron from XSD and does not overwrite existing target file.
XSpecCompileTask: compile.xspecto executable runnerXSpecTask: run XSpec and emit XML + JUnit reports- Current built-in scope: XSLT XSpec
- XSLT scenario: direct
.xspeccompile/run flow. - XQuery scenario: validate XQuery outputs through integration tasks, then assert results in test layer (JUnit/TestKit).
- XSD scenario: run
XsdTask, assert SVRL/JUnit outputs in pipeline. - Schematron scenario: run
SchematronTask, assert SVRL/JUnit outputs, optionally combine with XSpec around downstream XSLT assets.
Groovy Task Def (XSpec compile + run)
tasks.register('compileXSpec', name.jurgenei.gradle.xml.XSpecCompileTask) {
source(fileTree('src/main/xspec') { include '**/*.xspec' })
outputDir.set(layout.buildDirectory.dir('generated/xspec'))
outputExtension.set('.xspec.xsl')
}
tasks.register('runXSpec', name.jurgenei.gradle.xml.XSpecTask) {
dependsOn tasks.named('compileXSpec')
source(fileTree('src/main/xspec') { include '**/*.xspec' })
outputDir.set(layout.buildDirectory.dir('reports/xspec'))
junitOutputDir.set(layout.buildDirectory.dir('reports/xspec/junit'))
failOnError.set(true)
}Kotlin Task Def (XSpec compile + run)
tasks.register<name.jurgenei.gradle.xml.XSpecCompileTask>("compileXSpec") {
source(fileTree("src/main/xspec") { include("**/*.xspec") })
outputDir.set(layout.buildDirectory.dir("generated/xspec"))
outputExtension.set(".xspec.xsl")
}
tasks.register<name.jurgenei.gradle.xml.XSpecTask>("runXSpec") {
dependsOn(tasks.named("compileXSpec"))
source(fileTree("src/main/xspec") { include("**/*.xspec") })
outputDir.set(layout.buildDirectory.dir("reports/xspec"))
junitOutputDir.set(layout.buildDirectory.dir("reports/xspec/junit"))
failOnError.set(true)
}- Java 21+
- Public API/classes documented with Javadoc
- Multiline literals use text blocks where practical
- Build scripts follow standard Gradle Groovy/Kotlin DSL idioms
./gradlew build
./gradlew test
./gradlew coverage
./gradlew allSecurityChecksCoverage outputs:
build/reports/jacoco/test/jacocoTestReport.xmlbuild/reports/jacoco/test/html/index.html
AbstractXmlTransformTask
├── XsltTask
├── XQueryTask
├── XSpecCompileTask
└── XSpecTask
AbstractXmlValidationTask
├── SchematronTask
└── XsdTask
See runnable examples under samples/:
samples/transformation/xsltsamples/transformation/xquerysamples/transformation/selsamples/transformation/shacl-selsamples/validation/xsdsamples/validation/schematronsamples/schematron-bootstrap-ooxml
Workflow and expectations: CONTRIBUTING.md