MTI TEK
  • Home
  • LLMs
  • Docker
  • Kubernetes
  • Java
  • All Tutorials
Code Samples | JAXB Code Generation from XML Schema with Custom Bindings
  1. Introduction and Overview
  2. Project Structure and Layout
  3. Maven Plugin Configuration for Code Generation
  4. Resource Loading Utilities
  5. JAXB Context Helper Methods
  6. XML Schema Validation Utilities
  7. JAXB Marshalling Utilities
  8. JAXB Unmarshalling Utilities
  9. XML Schema Definition (XSD)
  10. Sample XML Data File
  11. Custom JAXB Bindings Configuration
  12. Logging Configuration
  13. Generated Classes to XML Marshalling Demo
  14. Generated Classes from XML Unmarshalling Demo

  1. Introduction and Overview
    This tutorial demonstrates how to use the JAXB (Jakarta XML Binding) API for XML data binding in modern Java applications. You'll learn essential techniques for converting between Java objects and XML representations using Jakarta EE standards.
    • Use the JAXB Maven plugin to generate Java classes from XML schemas:
      Configure automated code generation with proper dependencies and customization bindings for enterprise development.
    • Customize JAXB Bindings to reference existing classes:
      Learn to create binding files (.xjb) that control package structure and integrate with existing codebases.
    The tutorial also demonstrates practical JAXB API usage with professional utility classes:
    • Marshal Java objects into XML data: Generated Classes to XML Marshalling Demo
      Convert Java objects into formatted XML with namespace handling, proper encoding, and schema compliance.
    • Unmarshal XML data into Java objects: Generated Classes from XML Unmarshalling Demo
      Parse XML documents with schema validation and automatically populate Java objects using modern error handling patterns.
    Note: The following files are provided as examples to demonstrate the usage of JAXB APIs:
    • payload.xsd - XML Schema definition.
    • payload.xml - Sample XML data file.
    • payload.xjb - Sample JAXB bindings configuration file.
    These sample files illustrate practical implementation patterns and can be adapted for your specific use cases.
  2. Project Structure and Layout
    Maven project layout showing the organized directory structure with source code, utility classes, resources, and generated code locations.
    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── mtitek
            │       └── jaxb
            │           ├── bindings
            │           │    ├── TestMarshaller.java
            │           │    ├── TestUnmarshaller.java
            │           └── utils
            │               ├── JAXBContextUtils.java
            │               ├── MarshallerUtils.java
            │               ├── ResourceUtils.java
            │               ├── SchemaUtils.java
            │               └── UnmarshallerUtils.java
            └── resources
                ├── logback.xml
                ├── payload.xjb
                ├── payload.xml
                └── payload.xsd
    
  3. Maven Plugin Configuration for Code Generation
    Complete pom.xml setup with Jakarta XML Binding dependencies, JAXB Maven plugin configuration for code generation, and build helper plugin integration.
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>mtitek.jaxb.bindings</groupId>
        <artifactId>mtitek-jaxb-bindings</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>mtitek-jaxb-bindings</name>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    
            <java.version>23</java.version>
    
            <maven.compiler.source>23</maven.compiler.source>
            <maven.compiler.target>23</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.18.0</version>
            </dependency>
    
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.19.0</version>
            </dependency>
    
            <dependency>
                <groupId>jakarta.xml.bind</groupId>
                <artifactId>jakarta.xml.bind-api</artifactId>
                <version>4.0.2</version>
            </dependency>
    
            <dependency>
                <groupId>org.glassfish.jaxb</groupId>
                <artifactId>jaxb-runtime</artifactId>
                <version>4.0.5</version>
            </dependency>
    
            <dependency>
                <groupId>org.glassfish.jaxb</groupId>
                <artifactId>jaxb-xjc</artifactId>
                <version>4.0.5</version>
            </dependency>
    
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.4.14</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.14.0</version>
                    <configuration>
                        <source>${maven.compiler.source}</source>
                        <target>${maven.compiler.target}</target>
                    </configuration>
                </plugin>
    
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <version>3.8.1</version>
                    <executions>
                        <execution>
                            <id>dependency-analyze</id>
                            <goals>
                                <goal>analyze</goal>
                            </goals>
                            <phase>package</phase>
                        </execution>
                    </executions>
                </plugin>
    
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>jaxb2-maven-plugin</artifactId>
                    <version>3.1.0</version>
                    <executions>
                        <execution>
                            <id>xjc</id>
                            <goals>
                                <goal>xjc</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <sources>
                            <source>${project.basedir}/src/main/resources/payload.xsd</source>
                        </sources>
                        <xjbSources>
                            <xjbSource>${project.basedir}/src/main/resources/payload.xjb</xjbSource>
                        </xjbSources>
    
                        <packageName>mtitek.jaxb.bindings.payload</packageName>
                        <outputDirectory>${project.build.directory}/generated-sources/jaxb</outputDirectory>
    
                        <clearOutputDir>true</clearOutputDir>
    
                        <verbose>true</verbose>
                        <extension>true</extension>
                    </configuration>
                    <dependencies>
                        <dependency>
                            <groupId>jakarta.xml.bind</groupId>
                            <artifactId>jakarta.xml.bind-api</artifactId>
                            <version>4.0.2</version>
                        </dependency>
                        <dependency>
                            <groupId>org.glassfish.jaxb</groupId>
                            <artifactId>jaxb-runtime</artifactId>
                            <version>4.0.5</version>
                        </dependency>
                        <dependency>
                            <groupId>org.glassfish.jaxb</groupId>
                            <artifactId>jaxb-xjc</artifactId>
                            <version>4.0.5</version>
                        </dependency>
                    </dependencies>
                </plugin>
    
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>build-helper-maven-plugin</artifactId>
                    <version>3.6.0</version>
                    <executions>
                        <execution>
                            <id>add-generated-sources</id>
                            <phase>generate-sources</phase>
                            <goals>
                                <goal>add-source</goal>
                            </goals>
                            <configuration>
                                <sources>
                                    <source>${project.build.directory}/generated-sources/jaxb</source>
                                </sources>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    
  4. Resource Loading Utilities
    Utility class for loading resources from classpath as streams or strings using Apache Commons IO.
    package mtitek.jaxb.utils;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.commons.io.IOUtils;
    
    public class ResourceUtils {
        private ResourceUtils() {
        }
    
        public static InputStream getResourceAsStream(final String fileName) {
            return Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
        }
    
        public static String getResourceAsString(final String fileName) throws IOException {
            try (final InputStream inputStream = ResourceUtils.getResourceAsStream(fileName)) {
                return IOUtils.toString(inputStream, "UTF-8");
            }
        }
    }
    
  5. JAXB Context Helper Methods
    Helper class to create JAXB contexts with support for multiple classes using varargs and Optional return types.
    package mtitek.jaxb.utils;
    
    import java.util.Optional;
    
    import jakarta.xml.bind.JAXBContext;
    import jakarta.xml.bind.JAXBException;
    
    import org.apache.commons.lang3.ArrayUtils;
    
    public class JAXBContextUtils {
        private JAXBContextUtils() {
        }
    
        public static <T> Optional<JAXBContext> getJAXBContext(final Class<T> mainClassToBeBound,
                final Class<?>... classesToBeBound) throws JAXBException {
            return Optional.ofNullable(JAXBContext.newInstance(ArrayUtils.add(classesToBeBound, mainClassToBeBound)));
        }
    }
    
  6. XML Schema Validation Utilities
    Utility for creating XML Schema objects from XSD files with proper resource management and stream handling.
    package mtitek.jaxb.utils;
    
    import java.io.IOException;
    import java.util.Optional;
    import java.util.stream.Stream;
    
    import javax.xml.XMLConstants;
    import javax.xml.transform.stream.StreamSource;
    import javax.xml.validation.Schema;
    import javax.xml.validation.SchemaFactory;
    
    import org.apache.commons.lang3.ArrayUtils;
    import org.xml.sax.SAXException;
    
    public class SchemaUtils {
        private SchemaUtils() {
        }
    
        public static Optional<Schema> getSchema(final String... filesNames) throws SAXException, IOException {
            Schema schema = null;
            StreamSource[] streamSources = null;
    
            if (!ArrayUtils.isEmpty(filesNames)) {
                try {
                    streamSources = Stream.of(filesNames).map(fileName -> ResourceUtils.getResourceAsStream(fileName))
                            .filter(inputStream -> inputStream != null).map(inputStream -> new StreamSource(inputStream))
                            .toArray(StreamSource[]::new);
    
                    if (!ArrayUtils.isEmpty(streamSources)) {
                        final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    
                        schema = schemaFactory.newSchema(streamSources);
                    }
                } finally {
                    if (!ArrayUtils.isEmpty(streamSources)) {
                        Stream.of(streamSources).forEach(streamSource -> {
                            try {
                                if (streamSource.getInputStream() != null) {
                                    streamSource.getInputStream().close();
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        });
                    }
                }
            }
    
            return Optional.ofNullable(schema);
        }
    }
    
  7. JAXB Marshalling Utilities
    Marshalling utility that converts Java objects to XML with optional QName support and formatted output.
    package mtitek.jaxb.utils;
    
    import java.io.OutputStream;
    import java.util.Optional;
    
    import jakarta.xml.bind.JAXBContext;
    import jakarta.xml.bind.JAXBElement;
    import jakarta.xml.bind.JAXBException;
    import jakarta.xml.bind.Marshaller;
    import javax.xml.namespace.QName;
    
    public class MarshallerUtils {
        private MarshallerUtils() {
        }
    
        public static <T> void marshal(final OutputStream outputStream, final T object, final QName qName,
                final Class<T> mainClassToBeBound, final Class<?>... classesToBeBound) throws JAXBException {
            final Optional<Marshaller> optionalMarshaller = MarshallerUtils.createMarshaller(mainClassToBeBound,
                    classesToBeBound);
    
            if (optionalMarshaller.isPresent()) {
                Marshaller marshaller = optionalMarshaller.get();
    
                if (qName == null) {
                    marshaller.marshal(object, outputStream);
                } else {
                    final JAXBElement<T> jaxbElement = new JAXBElement<>(qName, mainClassToBeBound, object);
                    marshaller.marshal(jaxbElement, outputStream);
                }
            } else {
                throw new JAXBException("Failed to create the marshaller!");
            }
        }
    
        private static <T> Optional<Marshaller> createMarshaller(final Class<T> mainClassToBeBound,
                final Class<?>... classesToBeBound) throws JAXBException {
            final Optional<JAXBContext> jaxbContext = JAXBContextUtils.getJAXBContext(mainClassToBeBound, classesToBeBound);
    
            Marshaller marshaller = null;
    
            if (jaxbContext.isPresent()) {
                marshaller = jaxbContext.get().createMarshaller();
    
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            }
    
            return Optional.ofNullable(marshaller);
        }
    }
    
  8. JAXB Unmarshalling Utilities
    Unmarshalling utility that converts XML to Java objects with schema validation and error handling.
    package mtitek.jaxb.utils;
    
    import java.io.InputStream;
    import java.util.Optional;
    
    import jakarta.xml.bind.JAXBContext;
    import jakarta.xml.bind.JAXBElement;
    import jakarta.xml.bind.JAXBException;
    import jakarta.xml.bind.Unmarshaller;
    import jakarta.xml.bind.helpers.DefaultValidationEventHandler;
    import javax.xml.transform.stream.StreamSource;
    import javax.xml.validation.Schema;
    
    public class UnmarshallerUtils {
        private UnmarshallerUtils() {
        }
    
        public static <T> Optional<T> unmarshal(final InputStream inputStream, final Schema schema,
                final Class<T> mainClassToBeBound, final Class<?>... classesToBeBound) throws JAXBException {
            if (inputStream == null) {
                throw new IllegalArgumentException("InputStream cannot be null");
            }
    
            final Optional<Unmarshaller> optionalUnmarshaller = UnmarshallerUtils.createUnmarshaller(mainClassToBeBound,
                    classesToBeBound);
    
            T instance = null;
    
            if (optionalUnmarshaller.isPresent()) {
                Unmarshaller unmarshaller = optionalUnmarshaller.get();
    
                if (schema != null) {
                    unmarshaller.setSchema(schema);
                    unmarshaller.setEventHandler(new DefaultValidationEventHandler());
                }
    
                final StreamSource streamSource = new StreamSource(inputStream);
    
                final JAXBElement<T> jaxbElement = unmarshaller.unmarshal(streamSource, mainClassToBeBound);
    
                if (jaxbElement != null) {
                    instance = jaxbElement.getValue();
                }
            } else {
                throw new JAXBException("Failed to create the unmarshaller!");
            }
    
            return Optional.ofNullable(instance);
        }
    
        private static <T> Optional<Unmarshaller> createUnmarshaller(final Class<T> mainClassToBeBound,
                final Class<?>... classesToBeBound) throws JAXBException {
            final Optional<JAXBContext> jaxbContext = JAXBContextUtils.getJAXBContext(mainClassToBeBound, classesToBeBound);
    
            Unmarshaller unmarshaller = null;
    
            if (jaxbContext.isPresent()) {
                unmarshaller = jaxbContext.get().createUnmarshaller();
            }
    
            return Optional.ofNullable(unmarshaller);
        }
    }
    
  9. XML Schema Definition (XSD)
    XML Schema Definition defining the payload structure with id, code, and optional parameters collection including nested elements and attributes.
    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema attributeFormDefault="unqualified"
        elementFormDefault="qualified"
        targetNamespace="http://bindings.jaxb.mtitek/payload"
        xmlns="http://bindings.jaxb.mtitek/payload"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb" jaxb:version="3.0">
    
        <xs:element name="payload">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="id" type="xs:string" />
                    <xs:element name="code" type="xs:string" />
                    <xs:element name="parameters" type="payloadParameters" minOccurs="0" maxOccurs="1" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    
        <xs:complexType name="payloadParameters">
            <xs:sequence>
                <xs:element name="parameter" type="payloadParameter" minOccurs="1" maxOccurs="unbounded" />
            </xs:sequence>
        </xs:complexType>
    
        <xs:complexType name="payloadParameter">
            <xs:all>
                <xs:element name="name" type="xs:string" />
                <xs:element name="value" type="xs:string" />
            </xs:all>
            <xs:attribute name="id" type="xs:string" />
        </xs:complexType>
    </xs:schema>
    
  10. Sample XML Data File
    Example XML document demonstrating the payload structure with namespace declarations, schema location, and sample parameter data.
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <payload xmlns="http://bindings.jaxb.mtitek/payload"
        xmlns:ns1="http://bindings.jaxb.mtitek/payload/data"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://bindings.jaxb.mtitek/payload payload.xsd http://bindings.jaxb.mtitek/payload/data payloadData.xsd">
        <id>123</id>
        <code>xyz</code>
    
        <parameters>
            <parameter id="11">
                <name>bar</name>
                <value>foo</value>
            </parameter>
    
            <parameter id="22">
                <value>ti</value>
                <name>ta</name>
            </parameter>
        </parameters>
    </payload>
    
  11. Custom JAXB Bindings Configuration
    JAXB binding customization file that controls package naming and code generation behavior for the payload schema.
    <?xml version="1.0" encoding="UTF-8"?>
    <jxb:bindings version="3.0" xmlns:jxb="https://jakarta.ee/xml/ns/jaxb"
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <jxb:bindings schemaLocation="payload.xsd" node="/xs:schema">
            <jxb:schemaBindings>
                <jxb:package name="mtitek.jaxb.bindings.payload"/>
            </jxb:schemaBindings>
        </jxb:bindings>
    </jxb:bindings>
    
  12. Logging Configuration
    Logging configuration for console output with structured formatting and appropriate log levels.
    <configuration>
        <appender name="stdout"
            class="ch.qos.logback.core.ConsoleAppender">
            <target>System.out</target>
    
            <encoder>
                <pattern>%p [%d{ISO8601}] %c - %m%n
                </pattern>
            </encoder>
        </appender>
    
        <logger name="mtitek.jaxb.bindings" level="INFO" />
    
        <root level="info">
            <appender-ref ref="stdout" />
        </root>
    </configuration>
    
  13. Generated Classes to XML Marshalling Demo
    Working example demonstrating how to create Java objects and marshal them to formatted XML output using the utility classes.
    package mtitek.jaxb.bindings;
    
    import java.io.ByteArrayOutputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    import jakarta.xml.bind.JAXBException;
    
    import mtitek.jaxb.bindings.payload.Payload;
    import mtitek.jaxb.bindings.payload.PayloadParameter;
    import mtitek.jaxb.bindings.payload.PayloadParameters;
    import mtitek.jaxb.utils.MarshallerUtils;
    
    /**
     * Test class demonstrating JAXB marshalling from Java objects to XML.
     */
    public class TestMarshaller {
    
        public static void main(String[] args) throws Exception {
            final Payload payload = initPayload();
    
            try (final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
                // Marshal payload to XML - null QName uses default namespace
                MarshallerUtils.marshal(byteArrayOutputStream, payload, null, Payload.class);
                System.out.println(byteArrayOutputStream.toString("UTF-8"));
            }
        }
    
        /**
         * Initialize test payload with sample data.
         */
        private static Payload initPayload() throws JAXBException {
            final Payload payload = new Payload();
            payload.setId("1");
            payload.setCode("111");
    
            final List<PayloadParameter> parameters = new ArrayList<>();
    
            // Add first parameter
            {
                final PayloadParameter parameter = new PayloadParameter();
                parameter.setId("1");
                parameter.setName("aaa");
                parameter.setValue("bbb");
                parameters.add(parameter);
            }
    
            // Add second parameter
            {
                final PayloadParameter parameter = new PayloadParameter();
                parameter.setId("2");
                parameter.setName("ccc");
                parameter.setValue("ddd");
                parameters.add(parameter);
            }
    
            final PayloadParameters payloadParameters = new PayloadParameters();
            payloadParameters.getParameter().clear();
            payloadParameters.getParameter().addAll(parameters);
            payload.setParameters(payloadParameters);
    
            return payload;
        }
    }
    
    Output:
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <payload xmlns="http://bindings.jaxb.mtitek/payload">
        <id>1</id>
        <code>111</code>
        <parameters>
            <parameter id="1">
                <name>aaa</name>
                <value>bbb</value>
            </parameter>
            <parameter id="2">
                <name>ccc</name>
                <value>ddd</value>
            </parameter>
        </parameters>
    </payload>
    
  14. Generated Classes from XML Unmarshalling Demo
    Working example showing how to unmarshal XML documents into Java objects with schema validation and error handling.
    package mtitek.jaxb.bindings;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Optional;
    
    import jakarta.xml.bind.JAXBException;
    import javax.xml.validation.Schema;
    
    import org.xml.sax.SAXException;
    
    import mtitek.jaxb.bindings.payload.Payload;
    import mtitek.jaxb.utils.ResourceUtils;
    import mtitek.jaxb.utils.SchemaUtils;
    import mtitek.jaxb.utils.UnmarshallerUtils;
    
    /**
     * Test class demonstrating JAXB unmarshalling from XML to Java objects.
     */
    public class TestUnmarshaller {
    
        public static void main(String[] args) throws SAXException, IOException, JAXBException {
            // Print the schema content for reference
            System.out.println(ResourceUtils.getResourceAsString("payload.xsd"));
    
            final Optional<Schema> schema = SchemaUtils.getSchema("payload.xsd");
    
            if (schema.isPresent()) {
                try (final InputStream inputStream = ResourceUtils.getResourceAsStream("payload.xml")) {
                    // Unmarshal XML to Java object with schema validation
                    final Optional<Payload> optionalPayload = UnmarshallerUtils.unmarshal(
                        inputStream, schema.get(), Payload.class);
    
                    if (optionalPayload.isPresent()) {
                        final Payload payload = optionalPayload.get();
                        System.out.println(payload);
    
                        // Print each parameter
                        payload.getParameters().getParameter().forEach(payloadParameter -> {
                            System.out.println("PayloadParameter [id=" + payloadParameter.getId()
                                + ", name=" + payloadParameter.getName()
                                + ", value=" + payloadParameter.getValue() + "]");
                        });
                    }
                }
            }
        }
    }
    
    Output:
    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema attributeFormDefault="unqualified"
        elementFormDefault="qualified"
        targetNamespace="http://bindings.jaxb.mtitek/payload"
        xmlns="http://bindings.jaxb.mtitek/payload"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb" jaxb:version="3.0">
    
        <xs:element name="payload">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="id" type="xs:string" />
                    <xs:element name="code" type="xs:string" />
                    <xs:element name="parameters" type="payloadParameters"
                        minOccurs="0" maxOccurs="1" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    
        <xs:complexType name="payloadParameters">
            <xs:sequence>
                <xs:element name="parameter" type="payloadParameter"
                    minOccurs="1" maxOccurs="unbounded" />
            </xs:sequence>
        </xs:complexType>
    
        <xs:complexType name="payloadParameter">
            <xs:all>
                <xs:element name="name" type="xs:string" />
                <xs:element name="value" type="xs:string" />
            </xs:all>
            <xs:attribute name="id" type="xs:string" />
        </xs:complexType>
    </xs:schema>
    mtitek.jaxb.bindings.payload.Payload@12e61fe6
    PayloadParameter [id=11, name=bar, value=foo]
    PayloadParameter [id=22, name=ta, value=ti]
    
© 2025 mtitek
About