A Gradle plugin providing Saxon-backed XSLT/XQuery transforms and SVRL-based XML validation tasks with an orthogonal, Gradle-style DSL.
Define and execute XPath/XSLT/XQuery transformations and XML validations as Gradle tasks with:
- File-tree input matching (include/exclude patterns)
- Explicit single-file mode via Ant-like
input(...)/output(...) - Output file generation with configurable extension mapping
- External parameter passing to transforms
- Optional parallel processing using virtual threads
- SVRL and optional JUnit XML reporting for validation
The plugin contributes four task types:
name.jurgenei.gradle.xml.XsltTask— XSLT 3.0 transformationsname.jurgenei.gradle.xml.XQueryTask— XQuery transformationsname.jurgenei.gradle.xml.SchematronTask— Schematron to SVRL validationname.jurgenei.gradle.xml.XsdTask— XSD validation normalized to SVRL
Both share a near-orthogonal API for unified Gradle-style configuration.
- Saxon HE XSLT 3.0 and XQuery execution
- Schematron validation via SchXslt2 transpiler (
name.dmaus.schxslt:schxslt2) - XSD validation with AUTO engine resolution (Saxon PE/EE when available, JAXP fallback on HE)
- Orthogonal task API — both task types inherit the same base configuration
- File-tree DSL — Ant-like include/exclude filtering via Gradle's native
fileTree - Single-file DSL — explicit one-to-one transforms via
input(...)andoutput(...) - Flexible output mapping — custom extension and output directory per task
- Parameter passing — externalize stylesheet/query variables
- Virtual-thread parallelism — optional worker pool for concurrent file processing (default: serial)
- Comprehensive testing — JUnit 4 integration tests with mirrored XSLT/XQuery scenarios
XsltTask and XQueryTask support two equivalent execution modes:
- File-tree mode: set
source(...)andoutputDir - Explicit single-file mode: set
input(...)andoutput(...)
Notes:
- In explicit mode,
input(...)andoutput(...)must be set together. - In file-tree mode,
outputDiris required. - Both modes support
param(...); file-tree mode additionally supportsworkersand extension-based mapping.
Validation tasks share a common contract (ValidationTaskSpec) and defaults:
outputExtension = '.svrl.xml'workers = 1reportFormat = SVRLfailOnError = truejunitOutputDir = build/reports/xml-validation/junit
ReportFormat values:
SVRLJUNITSVRL_AND_JUNIT
XsdTask supports XsdEngine values:
AUTO(default; prefers Saxon schema-aware, otherwise JAXP)SAXONJAXP
- Supported plugin ID:
name.jurgenei.gradle.xml - Maven artifact for legacy
buildscriptusage:name.jurgenei.gradle:gradle-xml-transform:<version> - Obsolete/legacy IDs from earlier docs are no longer supported.
Add to build.gradle.kts:
plugins {
id("name.jurgenei.gradle.xml")
}Or build.gradle:
plugins {
id 'name.jurgenei.gradle.xml'
}Legacy buildscript usage:
buildscript {
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath("name.jurgenei.gradle:gradle-xml-transform:0.1.1")
}
}
apply(plugin = "name.jurgenei.gradle.xml")plugins {
id("name.jurgenei.gradle.xml")
}
tasks.register<name.jurgenei.gradle.xml.XsltTask>("transformDocs") {
style("src/main/xslt/main.xsl")
source(fileTree("src/main/xml") {
include("**/*.xml")
exclude("**/legacy/**")
})
outputDir.set(layout.buildDirectory.dir("generated/xslt"))
outputExtension.set(".html")
workers.set(4)
param("env", "dev")
}
tasks.register<name.jurgenei.gradle.xml.XQueryTask>("queryDocs") {
query("src/main/xquery/main.xq")
source("src/main/xml/single.xml")
outputDir.set(layout.buildDirectory.dir("generated/xquery"))
outputExtension.set(".xml")
workers.set(1)
param("tenant", "acme")
}
tasks.register<name.jurgenei.gradle.xml.XsltTask>("transformOne") {
style("src/main/xslt/main.xsl")
input("src/main/xml/a.xml")
output("build/custom/b.xml")
}
tasks.register<name.jurgenei.gradle.xml.XQueryTask>("queryOne") {
query("src/main/xquery/main.xq")
input("src/main/xml/a.xml")
output("build/custom/b.xml")
}plugins {
id 'name.jurgenei.gradle.xml'
}
tasks.register('transformDocs', name.jurgenei.gradle.xml.XsltTask) {
style 'src/main/xslt/main.xsl'
source(fileTree('src/main/xml') {
include '**/*.xml'
exclude '**/legacy/**'
})
outputDir.set(layout.buildDirectory.dir('generated/xslt'))
outputExtension.set('.html')
workers.set(4)
param 'env', 'dev'
}
tasks.register('queryDocs', name.jurgenei.gradle.xml.XQueryTask) {
query 'src/main/xquery/main.xq'
source 'src/main/xml/single.xml'
outputDir.set(layout.buildDirectory.dir('generated/xquery'))
outputExtension.set('.xml')
workers.set(1)
param 'tenant', 'acme'
}
tasks.register('transformOne', name.jurgenei.gradle.xml.XsltTask) {
style 'src/main/xslt/main.xsl'
input 'src/main/xml/a.xml'
output 'build/custom/b.xml'
}
tasks.register('queryOne', name.jurgenei.gradle.xml.XQueryTask) {
query 'src/main/xquery/main.xq'
input 'src/main/xml/a.xml'
output 'build/custom/b.xml'
}tasks.register('validateSchematron', name.jurgenei.gradle.xml.SchematronTask) {
schema 'src/main/schematron/rules.sch'
source(fileTree('src/main/xml') { include '**/*.xml' })
outputDir.set(layout.buildDirectory.dir('reports/schematron'))
reportFormat.set(name.jurgenei.gradle.xml.validation.ReportFormat.SVRL_AND_JUNIT)
workers.set(4)
failOnError.set(false)
}
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'))
reportFormat.set(name.jurgenei.gradle.xml.validation.ReportFormat.SVRL_AND_JUNIT)
engine.set(name.jurgenei.gradle.xml.validation.XsdEngine.AUTO)
}gradle testGenerate coverage report and enforce the current minimum line coverage baseline (>= 0%):
./gradlew coverageCoverage report outputs:
- XML:
build/reports/jacoco/test/jacocoTestReport.xml - HTML:
build/reports/jacoco/test/html/index.html
CI coverage workflow: .github/workflows/coverage.yml
To enable Codecov upload/badge, add repository secret CODECOV_TOKEN.
gradle buildRequired Java version: 21+
AbstractXmlTransformTask (shared base)
├── XsltTask (XSLT transformations)
└── XQueryTask (XQuery transformations)
AbstractXmlValidationTask (shared base)
├── SchematronTask (Schematron validation)
└── XsdTask (XSD validation)
- Resolve input files from
source/fileset - Sort files deterministically
- Optionally parallelize using virtual-thread worker pool (if
workers > 1) - For each input file:
- Derive output file path using
outputExtensionmapping - Create output directories (thread-safe via
Files.createDirectories) - Compile and execute transform (XSLT or XQuery)
- Log success or collect failure
- Derive output file path using
workers = 1(default): Sequential processingworkers > 1: Fixed virtual-thread pool with concurrent file processing
Virtual threads are used to maximize throughput with minimal memory overhead for I/O-bound XML transformations.
Runnable minimal examples are available under samples/:
samples/xslt-basicsamples/xquery-basicsamples/validation-basic
See samples/README.md for run commands.
JUnit 4 with Gradle TestKit for functional integration testing:
gradle test --tests '*XsltTaskIntegrationTest'
gradle test --tests '*XQueryTaskIntegrationTest'
gradle test --tests '*SchematronTaskIntegrationTest'
gradle test --tests '*XsdTaskIntegrationTest'- Java 21+ source
- Javadoc on all public APIs and classes
- Text blocks for multiline strings (Java 15+)
Contribution workflow and coding expectations are documented in CONTRIBUTING.md.