In my previous post I showed how to create RESTful services using Spring Framework. For representation of resources in XML I used JAXB and I followed the bottom-up approach (I wrote the Java classes and I let to generate XML/XSD from Java classes). In this post I will demonstrate how you can generate the same Java classes (User
and UserList
) from XML Schema (XSD) during Maven build, therefore using a top-down approach. For generation of Java classes from XML Schema during Maven build I will use Java.net Maven 2 JAXB 2 Plugin.
Along with the default generation of Java classes from XML Schema using xjc
tool, I will add the following customizations:
- Making all the generated classes to implement
Serializable
(useful when you want to use classes with some remote services: RMI, EJB, etc). - Use
java.util.Calendar
instead ofjavax.xml.datatype.XMLGregorianCalendar
for fields generated from elements of typexs:dateTime
. - Generated classes should also have a generated
toString()
method. - Classes have to be annotated with
XmlRootElement
(required by Spring Framework when classes are used to represent state in RESTful services). - You can further enhance and customize the generation of Java classes using various plugins.
Project sources are available for download. So let’s start, step by step:
- Create Maven project. Below you can see the POM:
<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>org.zmeu</groupId> <artifactId>zmeu-blog-maven-jaxb</artifactId> <version>1.0-SNAPSHOT</version> <name>ZMEU Blog Maven JAXB</name> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <version>0.8.0</version> <configuration> <schemaDirectory>src/main/resources/schema</schemaDirectory> <bindingDirectory>src/main/resources/schema</bindingDirectory> <generatePackage>org.zmeu.blog.jaxb</generatePackage> <strict>false</strict> <extension>true</extension> <plugins> <plugin> <groupId>org.jvnet.jaxb2_commons</groupId> <artifactId>jaxb2-basics</artifactId> <version>0.6.2</version> </plugin> <plugin> <groupId>org.jvnet.jaxb2_commons</groupId> <artifactId>jaxb2-basics-annotate</artifactId> <version>0.6.2</version> </plugin> </plugins> <args> <arg>-Xannotate</arg> <arg>-XtoString</arg> </args> </configuration> <executions> <execution> <id>generate</id> <goals> <goal>generate</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.jvnet.jaxb2_commons</groupId> <artifactId>jaxb2-basics-runtime</artifactId> <version>0.6.2</version> </dependency> </dependencies> </project>
- Write XML Schema (
schema.xsd
):<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="user" type="user" /> <xs:element name="userList" type="userList" /> <xs:complexType name="user"> <xs:all> <xs:element name="id" type="xs:long" minOccurs="0" /> <xs:element name="name" type="xs:string" /> <xs:element name="registrationDate" type="xs:dateTime" /> </xs:all> </xs:complexType> <xs:complexType name="userList"> <xs:sequence> <xs:element name="user" type="user" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> </xs:schema>
- Customize JAXB Bindings (
binding.xjb
):<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:annox="http://annox.dev.java.net" xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd" version="2.1"> <jaxb:globalBindings> <!-- Use Calendar instead of XMLGregorianCalendar for xs:dateTime --> <jaxb:javaType name="java.util.Calendar" xmlType="xs:dateTime" parseMethod="javax.xml.bind.DatatypeConverter.parseDateTime" printMethod="javax.xml.bind.DatatypeConverter.printDateTime" /> <!-- Force all classes implements Serializable --> <xjc:serializable uid="1" /> </jaxb:globalBindings> <!-- Annotate the following classes with XmlRootElement --> <jaxb:bindings schemaLocation="schema.xsd" node="/xs:schema"> <jaxb:bindings node="xs:complexType[@name='user']"> <annox:annotate> <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="user" /> </annox:annotate> </jaxb:bindings> <jaxb:bindings node="xs:complexType[@name='userList']"> <annox:annotate> <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="userList" /> </annox:annotate> </jaxb:bindings> </jaxb:bindings> </jaxb:bindings>
- Run the build using
mvn clean install
command. Build must be successful. Generated classes will be located intarget/generated-sources/xjc
directory. Below is a snippet from generatedUser
class:..... @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "user", propOrder = {}) @XmlRootElement(name = "user") public class User implements Serializable, ToString { private final static long serialVersionUID = 1L; protected Long id; @XmlElement(required = true) protected String name; @XmlElement(required = true, type = String.class) @XmlJavaTypeAdapter(Adapter1 .class) @XmlSchemaType(name = "dateTime") protected Calendar registrationDate; ..... }
- You are done!
Update (01-Jun-2013): In case you have several schema files and you wish to generate java classes in different packages then you have the option to specify the package inside JAXB binding file (*.xjb)
. For this you have to:
- Remove
generatePackage
element frommaven-jaxb2-plugin
configuration. In our case remove the line:<generatePackage>org.zmeu.blog.jaxb</generatePackage>
- Add the desired package name for every schema file inside JAXB binding file
(*.xjb)
as follows (see the highlighted section):<jaxb:bindings schemaLocation="schema.xsd" node="/xs:schema"> <jaxb:schemaBindings> <jaxb:package name="org.zmeu.blog.jaxb.user" /> </jaxb:schemaBindings> <jaxb:bindings node="xs:complexType[@name='user']"> <annox:annotate> <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="user" /> </annox:annotate> </jaxb:bindings> <jaxb:bindings node="xs:complexType[@name='userList']"> <annox:annotate> <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="userList" /> </annox:annotate> </jaxb:bindings> </jaxb:bindings>
KB at 34.2 KB/sec)
ReplyDeleteINFO] ------------------------------------------------------------------------
INFO] BUILD FAILURE
INFO] ------------------------------------------------------------------------
INFO] Total time: 1:03.273s
INFO] Finished at: Wed Aug 01 10:16:02 BST 2012
INFO] Final Memory: 3M/7M
INFO] ------------------------------------------------------------------------
ERROR] Failed to execute goal org.jvnet.jaxb2.maven2:maven-jaxb2-plugin:0.8.0:generate (generate) on project zmeu-blog-maven-jaxb: Execution generate of goal o
g.jvnet.jaxb2.maven2:maven-jaxb2-plugin:0.8.0:generate failed: An API incompatibility was encountered while executing org.jvnet.jaxb2.maven2:maven-jaxb2-plugin
0.8.0:generate: java.lang.LinkageError: JAXB 2.1 API is being loaded from the bootstrap classloader, but this RI (from jar:file:/C:/Users/akisanyas/.m2/reposit
ry/com/sun/xml/bind/jaxb-impl/2.2.4-1/jaxb-impl-2.2.4-1.jar!/com/sun/xml/bind/v2/model/impl/ModelBuilder.class) needs 2.2 API. Use the endorsed directory mecha
ism to place jaxb-api.jar in the bootstrap classloader. (See http://java.sun.com/j2se/1.6.0/docs/guide/standards/)
ERROR] -----------------------------------------------------
ERROR] realm = plugin>org.jvnet.jaxb2.maven2:maven-jaxb2-plugin:0.8.0
ERROR] strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
ERROR] urls[0] = file:/C:/Users/akisanyas/.m2/repository/org/jvnet/jaxb2/maven2/maven-jaxb2-plugin/0.8.0/maven-jaxb2-plugin-0.8.0.jar
ERROR] urls[1] = file:/C:/Users/akisanyas/.m2/repository/org/jvnet/jaxb2/maven2/maven-jaxb2-plugin-core/0.8.0/maven-jaxb2-plugin-core-0.8.0.jar
ERROR] urls[2] = file:/C:/Users/akisanyas/.m2/repository/com/sun/org/apache/xml/internal/resolver/20050927/resolver-20050927.jar
ERROR] urls[3] = file:/C:/Users/akisanyas/.m2/repository/backport-util-concurrent/backport-util-concurrent/3.1/backport-util-concurrent-3.1.jar
ERROR] urls[4] = file:/C:/Users/akisanyas/.m2/repository/org/codehaus/plexus/plexus-interpolation/1.11/plexus-interpolation-1.11.jar
ERROR] urls[5] = file:/C:/Users/akisanyas/.m2/repository/org/codehaus/plexus/plexus-utils/1.5.15/plexus-utils-1.5.15.jar
ERROR] urls[6] = file:/C:/Users/akisanyas/.m2/repository/junit/junit/4.8.1/junit-4.8.1.jar
ERROR] urls[7] = file:/C:/Users/akisanyas/.m2/repository/org/jfrog/maven/annomojo/maven-plugin-anno/1.3.1/maven-plugin-anno-1.3.1.jar
ERROR] urls[8] = file:/C:/Users/akisanyas/.m2/repository/org/jvnet/jaxb2/maven2/maven-jaxb22-plugin/0.8.0/maven-jaxb22-plugin-0.8.0.jar
ERROR] urls[9] = file:/C:/Users/akisanyas/.m2/repository/com/sun/xml/bind/jaxb-impl/2.2.4-1/jaxb-impl-2.2.4-1.jar
ERROR] urls[10] = file:/C:/Users/akisanyas/.m2/repository/javax/xml/bind/jaxb-api/2.2.3/jaxb-api-2.2.3.jar
ERROR] urls[11] = file:/C:/Users/akisanyas/.m2/repository/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2.jar
ERROR] urls[12] = file:/C:/Users/akisanyas/.m2/repository/javax/activation/activation/1.1/activation-1.1.jar
ERROR] urls[13] = file:/C:/Users/akisanyas/.m2/repository/com/sun/xml/bind/jaxb-xjc/2.2.4-1/jaxb-xjc-2.2.4-1.jar
ERROR] Number of foreign imports: 1
ERROR] import: Entry[import from realm ClassRealm[maven.api, parent: null]]
ERROR]
ERROR] -----------------------------------------------------
ERROR] -> [Help 1]
ERROR]
ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
ERROR] Re-run Maven using the -X switch to enable full debug logging.
ERROR]
ERROR] For more information about the errors and possible solutions, please read the following articles:
ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginContainerException
I am not sure what to do about this please advise.
ReplyDeleteIt seems that you have a conflict with JAXB dependencies defined in your project. I see that you are using jaxb-api:2.2.3 and jaxb-impl:2.2.4-1.
DeleteThe problem is that Java 6 includes JAXB 2.1, so there is no need to include it at all in your project. Here http://jaxb.java.net/guide/Which_JAXB_RI_is_included_in_which_JDK_.html you can see which JAXB RI is included in which JDK.
In case you still require to use a specific version of JAXB and you encounter such errors then you may try to put the jaxb-api.jar that you're trying to use into JDK_HOME/jre/lib/endorsed. See http://blog.spaceprogram.com/2007/05/how-to-fix-linkageerror-when-using-jaxb.html for more details.
Great post - not having the @XmlRootElement annotation was causing me a headache with Spring (using the RestTemplate requires the annotation to be attached).
ReplyDeleteThanks very much
Thanks.
DeleteGreat tutorial, thanks.
ReplyDeleteNext URL will help to prevent some doubts regarding where we have to place *.xjb file:
https://java.net/projects/maven-jaxb2-plugin/pages/Home
I mentioned in the post itself that you can download the entire maven project used in this blog post. There you may see full pom.xml and where all the files resides.
DeleteIn your case the location of *.xsd and *.xjb files are defined in maven-jaxb2-plugin plugin configuration in pom.xm as follows:
<schemaDirectory>src/main/resources/schema</schemaDirectory>
<bindingDirectory>src/main/resources/schema</bindingDirectory>
Good one!! I am using a similar setting for date conversion to java.util.Calendar. The classes are auto generated by maven. But my problem is whenever I give a blank date it creates unmarshalling error.(like ) it gives Unmarshalling Error: java.lang.IllegalArgumentException: . How can I customize the generated Adapter1.class that is generated (public class Adapter1
ReplyDeleteextends XmlAdapter) to avoid the illegal argument exception
You may try to specify your own custom converters. Change
Delete<jaxb:javaType name="java.util.Calendar" xmlType="xs:dateTime"
parseMethod="javax.xml.bind.DatatypeConverter.parseDateTime"
printMethod="javax.xml.bind.DatatypeConverter.printDateTime" />
to use your own parseMethod and printMethod.
I have a schema where the element and the attribute share the same identifier in a particular complex type. I want to rename the attributes identifier in the generated pojo ,I understand we can achieve t by using the bindings individually,
ReplyDeleteIs there a way I can do it by replacing recursively in all the complex type wherever it conflicts Or Rename a particular attribute wherever it occurs in an Schema without mentioning the complex type.
Hi I wanted to know can I write the below code
ReplyDeleteannox:annotate
annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement"
name="{@id}"
annox:annotate
in such a way that when it generated class suppose there are two User and Userlist then @XmlRootElement will pick up the respective object name like for user @XmlRootElement(name='user') and for userlist @XmlRootElement(name='userList') respectively , is there any way to write the standard annotation .Please help I googled I alot very new to jaxb , dont find a solution.
Thanks in advance
Code was not visible while posting so I removed the angular tags sorry about that
DeleteIs there a way to put prefix to the generated package name?
ReplyDeleteWhat do you mean? You specify the package either globally in your pom.xml using generatePackage element or separately for every xsd in your xjb files using jaxb:schemaBindings element. Both cases are described above.
DeleteHi
ReplyDeleteThanks for the tutorial.
I have a big set of XSDs built over a period of many years, which take about 2 hours to compile.
Is there a way to add the previous version of the jar to the classpath and build only the changed XSDs with each build?
Thanks
sanath.
Please note that these files are not independent and the dependencies are scattered in all the files.
DeleteUnless you do a clean build, the plugin will generate the classes only for the schemas that has been changed. Check the plugin options for more details: http://confluence.highsource.org/display/MJIIP/User+Guide#UserGuide-Controllingtheoutput
DeleteIf we generate java classes from XSD using JAXB by default it's generating java class with protected members. But i want it should be private. Any idea? How can we modify the default behavior?
ReplyDeleteCheck this, maybe it will help you http://stackoverflow.com/q/9377923
DeleteHi am trying to write common methods tostring using custom strategy could you help on that... The custom class is not referring ... Plz some example..
ReplyDeleteYou can specify your custom strategy in the maven-jaxb2-plugin as following:
Delete<args>
<arg>-Xannotate</arg>
<arg>-XtoString</arg>
<arg>-XtoString-toStringStrategyClass=com.mypackage.MyCustomJAXBToStringStrategy</arg>
</args>
Here your custom strategy class has to extend org.jvnet.jaxb2_commons.lang.JAXBToStringStrategy
Hi,
ReplyDeleteI am using xjc(2.2.4-2) with jdk1.7.0_79. after generating class files from XSD, i can see some extra strings are getting added to all class files. like public JAXBElement getActor() {
return actor;
}
I tried with binding.xjb. still no luck. could you please check this and share the proper.xjb files for removing .
Thanks,
I’m looking forward for your next post, I’ll try to get the hang of it!
ReplyDelete출장안마
I’m curious to find out what blog system you have been working with? 타이마사지
ReplyDeleteHello, I am one of the most impressed people in your article. 슬롯사이트 If possible, please visit my website as well. Thank you.
ReplyDeleteDachshunds are bred and shown in two sizes: Standard and Miniature. https://www.cutespupsforsale.com/ Standard Dachshunds of all varieties (Smooth, Wirehair, and Longhair) usually weigh between 16 and 32 pounds. Miniature Dachshunds of all varieties weigh 11 pounds and under at teacup poodle for sale maturity. Dachshunds that weigh between 11 and 16 pounds are called Tweenies. Some people who breed exceptionally small Dachshunds advertise them as Toy Dachshunds, but this is purely a poodles for sale marketing term, not a recognized designation. He's bred for perseverance, which is another way of saying that he can be stubborn. Dachshunds have a reputation for being dachshund puppies sale entertaining and fearless, but what they want most is to cuddle with their people. Longhairs are calm and quiet, and Smooths have dachshund for sale a personality that lies somewhere in between. https://Greenlandpuppies.com Some Mini Dachshunds can be nervous or shy, but this isn't correct for the breed. Avoid puppies that show these characteristics.Like every dog, Dachshunds need early socialization-exposure to many different people, dachshund puppies for sale near me sights, sounds, and experiences-when they're young. Socialization helps ensure that your Dachshund puppy grows up to be a well-rounded dog. .
ReplyDeletevery nice article. thank you for sharing this post. who want to learn online courses. visit:
ReplyDeletePython Online Training
This Content is simply amazing, and helpful for students and business people. Gathered lots of information and waiting to see more updates.
ReplyDeleteRPA Training in Chennai
RPA Online Course
RPA Training in Coimbatore
It was easy for me to follow your article . I hope to see more articles similar to yours . It was easy to apply online for an e visa of Turkey and it saved me time and money at the same time.
ReplyDelete