This Gradle plugin allows integrating XJC code generation into a Gradle build.

Prerequisites

You need at least the following:

  • Gradle 5.6 or higher

  • JDK 8 or higher (for running Gradle)

Quick Start

Apply the org.unbroken-dome.xjc plugin to your Gradle project:

Groovy
plugins {
    id 'org.unbroken-dome.xjc' version '2.0.0'
}
Kotlin
plugins {
    id("org.unbroken-dome.xjc") version "2.0.0"
}

Put your XJC source files into src/main/schema. This includes XML schema files (extension .xsd), binding customizations (extension .xjb) and catalog files (extension .cat).

Add a dependency on jaxb-api to the implementation configuration of your source set.

Groovy
dependencies {
    implementation 'javax.xml.bind:jaxb-api:2.3.0'
}
Kotlin
dependencies {
    implementation("javax.xml.bind:jaxb-api:2.3.0")
}
If you are developing a Java library, and the generated classes should be part of the library’s public API, then you should use the api configuration instead.

Now, whenever the project’s sources are built, XJC will automatically be invoked to generate Java files and include them in the compilation.

Using Source Sets

Many Gradle Java projects use multiple source sets. This includes the main source set for the project’s production code, as well as the test source set for unit tests by default, and can easily be extended with additional, custom source sets in a Gradle build script.

Support for Multiple Source Sets

The org.unbroken-dome.xjc plugin adds XJC functionality for each source set in the project, both for existing source sets and source sets that are added after the plugin is applied.

For each source set there will be a task of type XjcGenerate that will do the actual work of invoking XJC for the source set. You will rarely need to call this task directly, because the plugin already sets up task dependencies for compileJava so that the generated sources are ready before the compilation starts.

Customizing Paths

By default, all XJC input files for a source set are expected in the src/<name>/schema directory, where <name> is the name of the source set. For example,

  • put XJC input files for the main source set into src/main/schema;

  • put XJC input files for the test source set into src/test/schema.

If you would like to change the default subdirectory name from schema to something else, you can do this conveniently by setting the xjcSrcDir property on the project’s global xjc extension. For example, to use the xjc subdirectory instead of schema:

Groovy
xjc {
    srcDirName = 'xjc'
}
Kotlin
xjc {
    srcDirName.set("xjc")
}

Each group of input files (schemas, binding customizations, catalogs, and URL sources) is also made available on the source set as a SourceDirectorySet for further customization:

  • xjcSchema for schema files

  • xjcBinding for binding customization files

  • xjcUrl for files listing remote schema URLs

  • xjcCatalog for catalog files

For example, to include another directory containing catalogs:

Groovy
sourceSets {
    main {
        xjcCatalog.srcDir("custom/catalog/path")
    }
}
Kotlin
sourceSets.named("main") {
    xjcCatalog.srcDir("custom/catalog/path")
}

Remote Schema Locations

XJC supports processing schemas from remote locations, in addition to schemas stored in the local file system.

To use such remote schemas with the org.unbroken-dome.xjc Gradle plugin, add a text file with the extension .url into your src/main/schema directory. The .url file should list one remote schema URL per line.

For example, to use the xmldsig schema from w3.org, add the following file to your project:

src/main/schema/xmldsig.url
https://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd

Note that this works best with schemas that are guaranteed to be immutable at the remote location. If your .url file or other input files do not change, the Gradle task will not pick up changes from the remote schema location, and will still report as "up to date" in the Gradle build.

To force a re-run of XJC even if your input files have not changed, use the --rerun-tasks flag when invoking Gradle.

In most cases, it is still recommended to manually download the .xsd files and put them into your source directory. This will make your build less dependent on outside circumstances or network connectivity, and thus more robust and reproducible.

XJC Versions

XJC has been around for a long time and comes in a variety of versions, including standard versions provided by Oracle and non-standard forks from third parties. They should usually correspond with the version of the JAXB API / runtime used, but it is also possible to use a newer XJC to generate code for use with an earlier JAXB target.

The org.unbroken-dome.xjc plugin supports the standard XJC versions 2.2, 2.3 and 3.0. Other versions may work as well but are not tested. The plugin will use XJC 2.3 by default if the version is not specified.

XJC 3.0 is currently in milestone status but since the interface to XJC has not really changed over the years, it should be safe to consider it stable. Nevertheless, the plugin will use XJC 2.3 as default until version 3.0 is released.

Selecting an XJC Version

The easiest way to select an XJC version is to use the property xjcVersion on the global xjc extension. The value should be one of the supported versions. This is a global setting because it is assumed that the same XJC version will be used for each source set.

For example, to use XJC 3.0:

Groovy
xjc {
    xjcVersion = '3.0'
}
Kotlin
xjc {
    xjcVersion.set("3.0")
}
When changing the XJC version, remember to also use an appropriate version of the JAXB API in your dependencies. The plugin doesn’t do this.

Changing the version with the xjcVersion property will effectively change the classpath that is used for invoking XJC, with the following dependencies:

XJC Version Classpath artifacts

2.2

com.sun.xml.bind:jaxb-xjc:2.2.11 com.sun.xml.bind:jaxb-core:2.2.11 com.sun.xml.bind:jaxb-impl:2.2.11 javax.xml.bind:jaxb-api:2.2.11

2.3

com.sun.xml.bind:jaxb-xjc:2.3.3

3.0

com.sun.xml.bind:jaxb-xjc:3.0.0-M4

Setting the XJC Tool Classpath Explicitly

You can also manually configure the classpath for invoking XJC by adding dependencies to the xjcTool configuration. As soon as this configuration contains any dependencies, the defaults will back away, and the xjc.xjcVersion property will have no effect.

For example:

Groovy
dependencies {
    xjcTool 'com.sun.xml.bind:jaxb-xjc:3.0.0-M4'
}
Kotlin
dependencies {
    "xjcTool"("com.sun.xml.bind:jaxb-xjc:3.0.0-M4")
}

Controlling XJC Behavior

The command line version of XJC supports a number of arguments to fine-tune the code generation. The XJC Gradle plugin supports many of these as properties which can be set in the build script.

The following properties are available in the project-scoped xjc block, and apply to all XJC invocations (for all source sets):

Property Description Equivalent xjc CLI flag Default

targetVersion (String)

The version of the JAXB specification to target.

-target

(use latest version)

docLocale (java.util.Locale)

The locale to be used when running XJC. This may influence the language of documentation comments in XJC-generated files.

(no equivalent)

JVM default locale

encoding (String)

The encoding for generated files.

-encoding (non-standard)

UTF-8

strictCheck (Boolean)

Perform strict schema validation.

-nv (if false)

true

packageLevelAnnotations (Boolean)

Generate package-level annotations into package-info.java files.

-npa (if false)

true

noFileHeader (Boolean)

Suppress the generation of a file header comment that includes some note and timestamp.

-no-header

true

enableIntrospection (Boolean)

Fix getter/setter generation to match the Bean introspection API.

-enableIntrospection (non-standard)

false

contentForWildcard (Boolean)

Generates content property for types with multiple xs:any derived elements (which is supposed to be correct behavior).

-contentForWildcard (non-standard)

false

readOnly (Boolean)

Write-protect the generated Java source files.

-readOnly

false

extension (Boolean)

Enable JAXB vendor extensions.

-extension

false (true if any -X…​ argument is present in extraArgs)

The following property is available for each source set:

Property Description Equivalent xjc CLI flag Default

xjcTargetPackage (String)

The target package for XJC.

-p

(not set)

Configuring XJC with Gradle Project Properties

As an alternative to configuring the above settings in your build script, you can use Gradle project properties, either from a gradle.properties file or passed on the command line using the -P switch. The property name correspond to the dot-separated path of the DSL properties.

Gradle properties are automatically picked up by subprojects in a multi-project build, so they are especially useful for configuring multiple projects at once.

For example:

gradle.properties
xjc.xjcVersion=2.3
xjc.targetVersion=2.2
xjc.docLocale=en
xjc.strictCheck=false
xjc.enableIntrospection=true
xjc.contentForWildcard=true
xjc.verbosity=quiet

To selectively override properties for a Gradle build, use the -P switch on the command line:

gradle build -Pxjc.verbosity=verbose
Using Gradle properties has lower precedence than explicitly setting them in your build script.

Using XJC Plugins

XJC allows hooking into and extending the code-generation process by using plugins. A plugin might, for example, add equals() and toString() methods into generated classes.

Some XJC plugins generate code that requires additional compile-time or runtime dependencies. Activating such plugins might require that you add these dependencies to your Gradle dependency configurations, e.g. implementation. Please check the documentation for these plugins for their additional dependencies.

Specifying the plugin classpath

The plugin JARs must be on the classpath for the xjc invocation. With the xjc Gradle plugin, you can do this very comfortably by adding dependencies to the xjcClasspath configuration.

For example, to use the JAXB2 Basics plugin:

Groovy
dependencies {
    xjcClasspath 'org.jvnet.jaxb2_commons:jaxb2-basics:0.12.0'
}
Kotlin
dependencies {
    "xjcClasspath"("org.jvnet.jaxb2_commons:jaxb2-basics:0.12.0")
}

When using multiple source sets with XJC, there will be a separate XJC plugin classpath configuration for each source set in the project. For the main sourceSet, the configuration is called xjcCatalogResolution as described above. For other source sets it will be called <name>XjcClasspath, for example, testXjcClasspath for the test source set.

In addition, the plugin creates a configuration called xjcClasspathGlobal, which is extended by all other plugin classpath configurations. Any plugin dependencies placed in this configuration will be picked up by XJC builds for all source sets.

Specifying extra arguments

Most XJC plugins need to be activated and/or configured using command-line arguments. You can specify these extra arguments using the xjcExtraArgs property on the source set. For example, to add the -Xequals and -XtoString arguments:

Groovy
sourceSets {
    main {
        xjcExtraArgs.addAll '-Xequals', '-XhashCode', '-XtoString'
    }
}
Kotlin
sourceSets.named("main") {
    xjcExtraArgs.addAll("-Xequals", "-XhashCode", "-XtoString")
}

The plugin will automatically switch on the JAXB extension mode if there are parameters starting with -X present, regardless of the extension option.

The project-scoped xjc block also has an extraArgs property. Arguments added on this level will be used by the XJC builds for all source sets. If both xjc.extraArgs and the source set’s xjcExtraArgs are used, then the global ones are added first, and the source-set-scoped ones afterwards.

For example, you could use a combination of the xjcClasspathGlobal configuration and the global extraArgs to apply and configure a plugin for all source sets:

Groovy
dependencies {
    xjcClasspathGlobal 'org.jvnet.jaxb2_commons:jaxb2-basics:0.12.0'
}

xjc {
    extraArgs.add '-Xequals', '-XhashCode', '-XtoString'
}
Kotlin
dependencies {
    "xjcClasspathGlobal"("org.jvnet.jaxb2_commons:jaxb2-basics:0.12.0")
}

xjc {
    extraArgs.add("-Xequals", "-XhashCode", "-XtoString")
}

Using Episodes

An episode file is a special file that contains information about the generated classes and can be imported by subsequent xjc runs to re-use the generated code. If the episode file is placed in the "magic" location META-INF/sun-jaxb.episode inside a JAR file, it can be picked up automatically by dependent builds. This is convenient when an import hierarchy of XML schemas should be reflected in a dependency hierarchy of JARs.

Producing an Episode File

To instruct XJC to produce an episode file, simply set the xjcGenerateEpisode property on the source set to true:

Groovy
sourceSets {
    main {
        xjcGenerateEpisode = true
    }
}
Kotlin
sourceSets.named("main") {
    xjcGenerateEpisode.set(true)
}

The output of the XJC generation will now include an episode file at META-INF/sun-jaxb.episode, which is set up as an input to the source set’s resources. As a consequence, this episode file will also end up in the JAR when generated from the main source set.

Consuming Episodes

Episode files are not consumed directly, but through JARs that contain them under META-INF/sun-jaxb.episode. To specify dependencies that contain episodes to be imported into the XJC build, add the libraries containing them to the xjcEpisode configuration:

Groovy
dependencies {
    xjcEpisode 'org.example:my-model:1.2.3'
}
Kotlin
dependencies {
    "xjcEpisode"("org.example:my-model:1.2.3")
}
Each source set will get its own episode dependency configuration. For the main source set, this configuration is called xjcEpisode; for other source sets it will be called <name>XjcEpisode. For example, to specify episode dependencies for the test source set, use the testXjcEpisode configuration.

These dependencies should resolve to JAR files; if they contain a META-INF/sun-jaxb.episode entry it will be imported by the current xjc invocation. If they don’t contain such a file they are simply ignored, so it is safe to pass a larger set of dependencies and have them scanned for episodes.

For example, you might simply want to scan the entire compileClasspath for any JARs containing episodes:

Groovy
dependencies {
    xjcEpisode configurations['compileClasspath']
}
Kotlin
dependencies {
    "xjcEpisode"(configurations["compileClasspath"])
}

Working with Catalogs

Catalogs can be used to map the URI of an imported schema (specified using <xsd:import>) to an actual URL or file from where it can be read. This is especially useful if the imported URI is only symbolic, or you cannot (or do not want to) change the importing schema.

Catalog files may be written in any of the formats understood by the Apache Commons XML Resolver, including OASIS TR9401 and OASIS XML Catalogs.

Catalog files located at src/<sourceSetName>/schema with the extension .cat are picked up automatically. If your files follow a different convention, you can modify the xjcCatalog source directory set to suit your needs:

Groovy
sourceSets {
    main {
        xjcCatalog {
            // pick up catalogs from a custom directory
            srcDir 'custom/catalog/path'
            // use the .catalog file extension
            include '**/*.catalog'
        }
    }
}
Kotlin
sourceSets.named("main") {
    xjcCatalog {
        // pick up catalogs from a custom directory
        srcDir("custom/catalog/path")
        // use the .catalog file extension
        include("**/*.catalog")
    }
}

In the catalog file, the following directives are most useful:

  • system to map one URI to another;

  • rewriteSystem to map a group of URIs by exchanging a prefix

catalog.cat
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
    <rewriteSystem
        systemIdStartString="http://schemas.example.com/"
        rewritePrefix="http://www.example.com/etc/schemas/"/>
</catalog>

The classpath: and maven: URI Schemes

This plugin supports two special URI schemes in catalogs, the classpath: and maven: scheme. They are inspired (and largely compatible) with the [JAXB2 Maven Plugin](https://github.com/highsource/maven-jaxb2-plugin), in order to simplify a migration from Maven to Gradle in projects that use XJC code generation.

The classpath: URI scheme

The classpath: scheme interprets the rest of the URI as the path to a classpath resource. This is especially useful for multi-step code generation where a library JAR contains the schema, an episode file and generated code:

Groovy
dependencies {
    implementation 'com.example:my-model:1.2.3'
    xjcEpisode configurations['compileClasspath']
}
Kotlin
dependencies {
    implementation("com.example:my-model:1.2.3")
    "xjcEpisode"(configurations["compileClasspath"])
}

Assuming the my-model JAR contains an XSD resource at schemas/my-model.xsd, you could write the catalog file as follows:

catalog.cat
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
    <rewriteSystem
        systemIdStartString="http://schemas.example.com/"
        rewritePrefix="classpath:schemas/"/>
</catalog>

Then, reference it in the importing schema:

schema.xsd
<!-- The schemaLocation will be mapped to the JAR classpath resource thanks to the catalog -->
<xsd:import namespace="http://schemas.example.com/mymodel"
            schemaLocation="http://schemas.example.com/my-model.xsd" />

All JARs in the special configuration xjcCatalogResolution are taken into account, which inherits all dependencies from compileClasspath by default. You can add additional dependencies to this configuration to enable catalog resolution from these artifacts.

When using multiple source sets with XJC, there will be a separate dependency configuration for each source set in the project. For the main sourceSet, the configuration is called xjcCatalogResolution as described above. For other source sets it will be called <name>XjcCatalogResolution, for example, testXjcCatalogResolution for the test source set.

In addition, the plugin creates a configuration called xjcCatalogResolutionGlobal, which does not have any dependencies by default. All other catalog resolution configurations extend this one, so you can add any dependencies that should be resolvable for all source sets to xjcCatalogResolutionGlobal.

The maven: URI scheme

The maven: scheme works similar to the classpath: scheme, but allows you to specify additional Maven coordinates to filter the dependency. The URI syntax is:

maven:<groupId>:<artifactId>:<extension>:<classifier>:<version>!path/to/resource

where all parts are optional, and trailing colons may be omitted.

Note that in contrast to the Maven JAXB2 Plugin, the dependency is not resolved ad-hoc: it must still be declared in the xjcCatalogResolution configuration (or inherited through other configurations).

You can think of the maven: scheme as an extension to classpath: with a filter for the JARs to be searched for resources. (In fact, classpath: is defined as an alias for maven::!.)

Migrating from Earlier Versions

Version 1.x of this plugin was centered around the XjcGenerate task, which was automatically created only for the main source set.

In version 2.0, most of the configuration has moved to the xjc DSL block in the project or the source set extension (properties on the source set starting with xjc), and XJC is enabled for all source sets automatically. You will rarely need to touch the XjcGenerate task directly with the new plugin version.

In many XJC scenarios, no further configuration is necessary at all, beyond applying the plugin and adding a dependency to the JAXB API.

Since the interface to XJC has not changed much, the available properties have stayed mostly the same, and it should not be difficult to figure out how to move them to the xjc block or the source set. Check the Controlling XJC Behavior section for a complete list of available properties.