├── 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
<?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>
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"); } } }
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))); } }
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); } }
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); } }
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); } }
<?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>
<?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>
<?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>
<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>
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>
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]