JAXB is a set of tools and libraries used to generate class libraries specifically to parse a defined XML schema. Java classes can be generated automatically for an XML schema as defined by an XSD file or can be coded manually.
This tutorial will show an example of using JAXB to generate classes automatically from an XSD. The generated library will be used for parsing of an XML file conforming to the specified XSD schema.
Two approaches can be taken. One is to use the Java JDK with the JAXB jar library and the other is to use the Java JDK exclusivly. Apache Ant script examples for each approach are provided below.
As a prerequisite the Java JDK need to be installed. See our tutorial on installing Java on Linux.
This example was compiled and run with jdk1.8.0_112
If using the JAXB jar libraries, download the latest JAXB jar libraries from: JAXB home page
cd /opt unzip ~/Downloads/jaxb-ri-2.2.11.zip
Update your ~/.bashrc:
.. ... # # JAXB # if [ -d /opt/jaxb-ri ] then PATH=/opt/jaxb-ri/bin:$PATH export CLASSPATH=/opt/jaxb-ri/lib/jaxb-xjc.jar:$CLASSPATH fi ... ..
Simple but less desirable (jar only and not complete SDK) alternative: The JAXB jaxb-xjc.jar, is available as a JAR file and can be placed in /opt/java/latest/lib/. See: FindJar.com
If using JBoss, your CLASSPATH can reference JAXB from the JBoss library: /opt/jboss-X.X.X.XXXXX/lib/jaxb-xjc.jar
Define an XML schema file (XSD) using your favorite XSD/XML editor:
File: corporation.xsd
Generate a Java class library files from the XSD: xjc -d ./ corporation.xsd
For a description of commands try: xjc -help or java -jar $JAVA_HOME/lib/jaxb-xjc.jar -help
The following ant snippet will be used to perform this task:
<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask"> <classpath refid="classpath" /> </taskdef> ... ... <xjc schema="corporation.xsd" package="com.megacorp.projectx.xml.jaxb" destdir="generated"> <produces dir="generated/com/megacorp/projectx/xml/jaxb" includes="**/*.java" /> </xjc>
This will generate the Java classes:
- gen-src/com/megacorp/projectx/xml/jaxb/Root.java
- gen-src/com/megacorp/projectx/xml/jaxb/EGender.java
- gen-src/com/megacorp/projectx/xml/jaxb/ELocation.java
- gen-src/com/megacorp/projectx/xml/jaxb/ERelation.java
- gen-src/com/megacorp/projectx/xml/jaxb/ETaxStatus.java
- gen-src/com/megacorp/projectx/xml/jaxb/ObjectFactory.java
File: src/com/megacorp/projectx/xml/TestJaxb.java
package com.megacorp.projectx.xml; import java.io.*; import java.util.Iterator; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import com.megacorp.projectx.xml.jaxb.Root; import com.megacorp.projectx.xml.JAXBXMLHandler; import com.megacorp.projectx.xml.JAXBXMLException; public class TestJaxb { static public void main (String[] args) { String fileName = "CorporationMegaX.xml"; try { readFile(fileName); } catch (Exception e) { System.out.println("Error! Exception caught"); e.printStackTrace(); } } public static void readFile(String fileName) { try { java.io.File XMLfile = new java.io.File(fileName); Root root = JAXBXMLHandler.unMarshal(Root.class,XMLfile); Root.Corporation corporation = root.getCorporation(); System.out.println("Corporation: " + corporation.getName()); System.out.println("Corp address: " + corporation.getAddress()); Root.People people = root.getPeople(); List<Root.People.Employee> employee = people.getEmployee(); System.out.println("There are " + employee.size() + " employees."); System.out.println("Employees:"); for(Iterator employeeIter = employee.iterator(); employeeIter.hasNext();) { Root.People.Employee employeeItem = (Root.People.Employee)employeeIter.next(); String name = employeeItem.getName(); System.out.println(" Employee Name: " + name); String taxStatus = employeeItem.getTaxStatus().value(); System.out.println(" Tax Status: " + taxStatus); System.out.println(" Gender: " + employeeItem.getGender().value()); String workPhone = employeeItem.getData().getWorkPhone(); System.out.println(" Work Phone: " + workPhone); if(taxStatus.equalsIgnoreCase("US-W2")) { System.out.println(" Employee Number: " + employeeItem.getUSW2().getEmpNumber()); System.out.println(" Manager: " + employeeItem.getUSW2().getManager()); System.out.println(" Start Year: " + employeeItem.getUSW2().getYearStart()); } else if(taxStatus.equalsIgnoreCase("US-1099")) { System.out.println(" SSN Number: " + employeeItem.getUS1099().getSsnNumber()); System.out.println(" Corp Name: " + employeeItem.getUS1099().getCorpName()); System.out.println(" Hire Relationship: " + employeeItem.getUS1099().getRelationship().value()); } else { System.out.println("No tax status specified"); } } } catch (Exception e) { System.out.println("Error! Exception caught"); e.printStackTrace(); } } }
File: src/com/megacorp/projectx/xml/JAXBXMLException.java
package com.megacorp.projectx.xml; /** * Checked exception thrown by Data Access Service layer to clients to report * system level errors. */ public class JAXBXMLException extends Exception { /** * Construct a JAXBXMLException with no arguments. */ public JAXBXMLException() { super(); } public JAXBXMLException(Exception e) { super(e); } /** * Construct a JAXBXMLException with the specified message ID. */ public JAXBXMLException( String msg ) { super( msg ); } /** * @param message * @param exception */ public JAXBXMLException(String message, Exception exception) { super(message, exception); } }
File: src/com/megacorp/projectx/xml/JAXBXMLHandler.java
package com.megacorp.projectx.xml; import java.io.*; import java.net.URL; import java.util.Arrays; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBElement; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.transform.stream.StreamSource; import com.megacorp.projectx.xml.JAXBXMLException; public class JAXBXMLHandler { public static <T> T unMarshal(Class<T> typeClass, File file) throws JAXBXMLException { try { JAXBContext jaxbContext = JAXBContext.newInstance(typeClass); Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); // jaxbUnmarshaller.setValidating( true ); T jaxbXmlObj = (T) jaxbUnmarshaller.unmarshal(file); return jaxbXmlObj; } catch (JAXBException e) { throw new JAXBXMLException(e); } catch (Exception e) { throw new JAXBXMLException(e); } } }
XML data file: CorporationMegaX.xml
<?xml version="1.0" encoding="UTF-8"?> <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="corporation.xsd"> <Corporation> <Name>Corporation MegaX</Name> <Phone>1-800-555-1212</Phone> <Fax>1-877-555-1212</Fax> <Address>512 Megacorp Way, Gotham City</Address> </Corporation> <People> <!-- The Boss --> <Employee TaxStatus="US-W2" Gender="Male" Desc="OnSite"> <Name>Mr Grand Kahuna</Name> <US-W2> <EmpNumber>1</EmpNumber> <Manager>None</Manager> <YearStart>1998</YearStart> </US-W2> <Data> <WorkPhone>1-800-555-1213</WorkPhone> <CellPhone>1-800-555-1214</CellPhone> <Address>100 Cherry Hill Lane, Gotham City</Address> </Data> </Employee> <!-- The Consultant --> <Employee TaxStatus="US-1099" Gender="Male" Desc="OnSite"> <Name>Mr Special Tee</Name> <US-1099> <SsnNumber>123-45-6788</SsnNumber> <Phone>1-817-555-1212</Phone> <CorpName>ABC Consulting</CorpName> <CorpAddress>3 Mockingbird Lane, Smallville AK</CorpAddress> <Relationship>CorpToIndividual</Relationship> </US-1099> <Data> <WorkPhone>1-800-555-1215</WorkPhone> <CellPhone>1-800-555-1216</CellPhone> <Address>200 Lookout Hill, Gotham City</Address> </Data> </Employee> <!-- The Secratary --> <Employee TaxStatus="US-W2" Gender="Female" Desc="OnSite"> <Name>Mrs Jenny Reliable</Name> <US-W2> <EmpNumber>2</EmpNumber> <Manager>Mr Grand Kahuna</Manager> <YearStart>1999</YearStart> </US-W2> <Data> <WorkPhone>1-800-555-1217</WorkPhone> <CellPhone>1-800-555-1218</CellPhone> <Address>300 Riverside View, Gotham City</Address> </Data> </Employee> </People> </root>
Two approaches are taken:
- # Reference the JAXB jar file library and use the Ant built-in "xjc" directive to generate the parser source
- # Reference the existing JAXB facilities within the Java JDK
1) Use the JAXB jar with the "xjc" directive:
Ant build script: build.xml<?xml version="1.0" encoding="utf-8"?> <project basedir="." default="run"> <property name="jaxb.home" value="." /> <path id="classpath"> <pathelement path="src" /> <pathelement path="classes" /> <pathelement path="schemas" /> <fileset dir="${jaxb.home}" includes="/opt/jaxb-ri/lib/*.jar" /> </path> <taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask"> <classpath refid="classpath" /> </taskdef> <!--generate Java source files--> <target name="jaxb" description="Generate JAXB classes"> <echo message="Compiling the schema..." /> <mkdir dir="gen-src/com/megacorp/projectx/xml/jaxb"/> <xjc schema="corporation.xsd" package="com.megacorp.projectx.xml.jaxb" destdir="gen-src"> <produces dir="gen-src/com/megacorp/projectx/xml/jaxb" includes="**/*.java" /> </xjc> </target> <!--compile Java source files--> <target name="compile" depends="jaxb" description="Compile all Java source files"> <echo message="Compiling the java source files..." /> <mkdir dir="classes" /> <javac destdir="classes" debug="on" includeAntRuntime="false"> <src path="src/com/megacorp/projectx/xml" /> <src path="gen-src" /> <classpath refid="classpath" /> </javac> </target> <target name="run" depends="compile" description="Run the sample app"> <echo message="Running the sample application..." /> <java classname="com.megacorp.projectx.xml.TestJaxb" fork="true"> <classpath refid="classpath" /> </java> </target> <target name="javadoc" description="Generates javadoc" depends="compile"> <echo message="Generating javadoc..." /> <mkdir dir="docs/api" /> <javadoc sourcepath="gen-src" destdir="docs/api" windowtitle="Using unmarshaller" useexternalfile="yes"> <fileset dir="." includes="gen-src/**/*.java"/> <!-- excludes="**/impl/**/*.java" / --> </javadoc> </target> <target name="clean" description="Deletes all the generated artifacts."> <delete dir="docs/api" /> <delete dir="gen-src" /> <delete dir="schemas" /> <delete dir="classes" /> </target> </project>
Compile: ant compile
[user1@tux JavaJaxb]$ ant compile ant compileBuildfile: /home/user1/Desktop/JavaJaxb/build.xml jaxb: [echo] Compiling the schema... [mkdir] Created dir: /home/user1/Desktop/JavaJaxb/gen-src/com/megacorp/projectx/xml/jaxb [xjc] Compiling file:/home/user1/Desktop/JavaJaxb/corporation.xsd [xjc] Writing output to /home/user1/Desktop/JavaJaxb/gen-src compile: [echo] Compiling the java source files... [javac] Compiling 9 source files to /home/user1/Desktop/JavaJaxb/classes [javac] Note: /home/user1/Desktop/JavaJaxb/src/com/megacorp/projectx/xml/JAXBXMLHandler.java uses unchecked or unsafe operations. [javac] Note: Recompile with -Xlint:unchecked for details. BUILD SUCCESSFUL Total time: 1 second
[Potential Pitfall]:
You may get the following error: taskdef class com.sun.tools.xjc.XJCTask cannot be found
This typically means that jaxb-xjc.jar is missing from your classpath
Run: ant run
[user1@tux JavaJaxb]$ ant run run: [echo] Running the sample application... [java] Corporation: Corporation MegaX [java] Corp address: 512 Megacorp Way, Gotham City [java] There are 3 employees. [java] Employees: [java] Employee Name: Mr Grand Kahuna [java] Tax Status: US-W2 [java] Gender: Male [java] Work Phone: 1-800-555-1213 [java] Employee Number: 1 [java] Manager: None [java] Start Year: 1998 [java] Employee Name: Mr Special Tee [java] Tax Status: US-1099 [java] Gender: Male [java] Work Phone: 1-800-555-1215 [java] SSN Number: 123-45-6788 [java] Corp Name: ABC Consulting [java] Hire Relationship: CorpToIndividual [java] Employee Name: Mrs Jenny Reliable [java] Tax Status: US-W2 [java] Gender: Female [java] Work Phone: 1-800-555-1217 [java] Employee Number: 2 [java] Manager: Mr Grand Kahuna [java] Start Year: 1999
2) Reference the Java JDK only:
This method does not require the JAXB jar file and relies on JAXB as it resides in the JDK. The only change in the Ant build script is the target "jaxb" which will execute the Java "xjc" compiler to generate the parser code.
Ant build script: build.xml<?xml version="1.0" encoding="utf-8"?> <project basedir="." default="run"> <property name="jaxb.home" value="." /> <path id="classpath"> <pathelement path="src" /> <pathelement path="classes" /> <pathelement path="schemas" /> <fileset dir="${jaxb.home}" includes="/opt/jaxb-ri/lib/*.jar" /> </path> <taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask"> <classpath refid="classpath" /> </taskdef> <!--generate Java source files--> <target name="jaxb" description="Generate JAXB classes"> <echo message="Compiling the schema..." /> <mkdir dir="gen-src/com/megacorp/projectx/xml/jaxb"/> <exec executable="xjc"> <arg value="-d"/> <arg value="gen-src/com/megacorp/projectx/xml/jaxb"/> <arg value="-p"/> <arg value="com.megacorp.projectx.xml.jaxb"/> <arg value="corporation.xsd"/> </exec> </target> <!--compile Java source files--> <target name="compile" depends="jaxb" description="Compile all Java source files"> <echo message="Compiling the java source files..." /> <mkdir dir="classes" /> <javac destdir="classes" debug="on" includeAntRuntime="false"> <src path="src/com/megacorp/projectx/xml" /> <src path="gen-src" /> <classpath refid="classpath" /> </javac> </target> <target name="run" depends="compile" description="Run the sample app"> <echo message="Running the sample application..." /> <java classname="com.megacorp.projectx.xml.TestJaxb" fork="true"> <classpath refid="classpath" /> </java> </target> <target name="javadoc" description="Generates javadoc" depends="compile"> <echo message="Generating javadoc..." /> <mkdir dir="docs/api" /> <javadoc sourcepath="gen-src" destdir="docs/api" windowtitle="Using unmarshaller" useexternalfile="yes"> <fileset dir="." includes="gen-src/**/*.java"/> <!-- excludes="**/impl/**/*.java" / --> </javadoc> </target> <target name="clean" description="Deletes all the generated artifacts."> <delete dir="docs/api" /> <delete dir="gen-src" /> <delete dir="schemas" /> <delete dir="classes" /> </target> </project>
Unlike Ant which explicitly specifies the configuration, Maven requires that the user operate under its convention. Maven expects to find source located in directories specified by its convention. Thus use the following directory layout:
pom.xml CorporationMegaX.xml src/main/java/com/megacorp/projectx/xml/TestJaxb.java JAXBXMLException.java JAXBXMLHandler.java src/main/resources/corporation.xsdMaven will generate the rest. Maven build script: pom.xml
<?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> <name>ProjecX</name> <groupId>com.megacorp.projectx</groupId> <artifactId>ProjectX</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <version>0.13.1</version> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <!-- Specify the package name of the generated sources instead of accepting the default. Deafault is the destination directory and the namespace: generated --> <generateDirectory>src/generated-sources/java</generateDirectory> <generatePackage>com.megacorp.projectx.xml.jaxb</generatePackage> <!-- Schema default location: src/main/resources/ One can specify a different location here: <schemaDirectory>src/main/resources</schemaDirectory> <schemaIncludes> <include>corporation.xsd</include> </schemaIncludes> --> </configuration> </plugin> </plugins> </build> </project>
Compile/build: mvn package
[user1@tux JavaJaxb]$ mvn package [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building ProjecX 1.0.0 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-jaxb2-plugin:0.13.1:generate (default) @ ProjectX --- [INFO] Up-to-date check for source resources [[file:/home/user1/ProjectX/src/main/resources/corporation.xsd, file:/home/user1/ProjectX/pom.xml]] and target resources [[file:/home/user1/ProjectX/src/generated-sources/java/com/megacorp/projectx/xml/jaxb/Root.java, file:/home/user1/ProjectX/src/generated-sources/java/com/megacorp/projectx/xml/jaxb/EGender.java, file:/home/user1/ProjectX/src/generated-sources/java/com/megacorp/projectx/xml/jaxb/ERelation.java, file:/home/user1/ProjectX/src/generated-sources/java/com/megacorp/projectx/xml/jaxb/ETaxStatus.java, file:/home/user1/ProjectX/src/generated-sources/java/com/megacorp/projectx/xml/jaxb/ObjectFactory.java, file:/home/user1/ProjectX/src/generated-sources/java/com/megacorp/projectx/xml/jaxb/ELocation.java, file:/home/user1/ProjectX/src/generated-sources/java/META-INF/sun-jaxb.episode]]. [INFO] Latest timestamp of the source resources is [2017-09-02 22:06:10.000], earliest timestamp of the target resources is [2017-09-02 22:06:22.000]. [INFO] Sources are up-to-date, XJC will be skipped. [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ ProjectX --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] Copying 1 resource [INFO] Copying 1 resource [INFO] [INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ ProjectX --- [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent! [INFO] Compiling 9 source files to /home/user1/ProjectX/target/classes [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ ProjectX --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /home/user1/ProjectX/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ ProjectX --- [INFO] No sources to compile [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ ProjectX --- [INFO] No tests to run. [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ ProjectX --- [INFO] Building jar: /home/user1/ProjectX/target/ProjectX-1.0.0.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.702s [INFO] Finished at: Sat Sep 02 22:08:33 PDT 2017 [INFO] Final Memory: 16M/359M [INFO] ------------------------------------------------------------------------
Run: java -cp ./target/ProjectX-1.0.0.jar com.megacorp.projectx.xml.TestJaxb
Corporation: Corporation MegaX Corp address: 512 Megacorp Way, Gotham City There are 3 employees. Employees: Employee Name: Mr Grand Kahuna Tax Status: US-W2 Gender: Male Work Phone: 1-800-555-1213 Employee Number: 1 Manager: None Start Year: 1998 Employee Name: Mr Special Tee Tax Status: US-1099 Gender: Male Work Phone: 1-800-555-1215 SSN Number: 123-45-6788 Corp Name: ABC Consulting Hire Relationship: CorpToIndividual Employee Name: Mrs Jenny Reliable Tax Status: US-W2 Gender: Female Work Phone: 1-800-555-1217 Employee Number: 2 Manager: Mr Grand Kahuna Start Year: 1999
Our example above is for a standard XML file containing only XML content. This is common for configuration files. This is specified in our example by the XML XSD schema:
<xs:element name="US-1099"> <xs:complexType mixed="false"> ... ... </xs:complexType>
One can also have mixed content where there is plain text around the XML content. The XSD would be stated as:
<xs:element name="US-1099"> <xs:complexType mixed="true"> ... ... </xs:complexType>
and the XML would have plain text mixed with the XML as in this example snippet:
<US-1099> SSN Number: <SsnNumber>123-45-6788</SsnNumber> Phone Number: <Phone>1-817-555-1212</Phone> Corporation: <CorpName>ABC Consulting</CorpName> Corp Adress: <CorpAddress>3 Mockingbird Lane, Smallville AK</CorpAddress> Relationship: <Relationship>CorpToIndividual</Relationship> </US-1099>
JAXB can handle a complexType with a "mixed" text of both "true" and "false" but only with a substantial change to the code. First note the the generated code is different:
- gen-src/com/megacorp/projectx/xml/jaxb/Root.java
- gen-src/com/megacorp/projectx/xml/jaxb/EGender.java
- gen-src/com/megacorp/projectx/xml/jaxb/ELocation.java
- gen-src/com/megacorp/projectx/xml/jaxb/ERelation.java
- gen-src/com/megacorp/projectx/xml/jaxb/ETaxStatus.java
- gen-src/com/megacorp/projectx/xml/jaxb/ObjectFactory.java
One can avoid the mixed text hassle by using XmlBeans or you can code for this conditions.
Here is a solid coding discussions on this topic:
- JAXB (info and download)
YoLinux Tutorials: