You Should Use JAXB Generated Classes for Restful Web Services

You Should Use JAXB Generated Classes for Restful Web Services

6 Comments

I remember in the late 90s / early 2000 all the buzz in programming was about XML. All the ‘experts’ said you had to be using XML for data exchange. XML does have many benefits. But often when programming today, XML is looked as ‘old school’ or too restrictive. XML sure can be finicky to work with. Many developers, when given a choice (including myself), will use JSON over XML. JSON is much more forgiving than XML is, which makes it simpler to work with.

While XML may be old school, it still can be good to use. Even when you’re doing JSON based Restful web services.

Restful Web Services in Spring

I’m not going to cover building Restful Web Services in Spring in this post. That’s a future topic. But when you’re using Spring MVC to develop Restful Web Services, you can set up a lot of flexibility. I typically design my web services so the client can use JSON or XML. The standards support this, and it’s fairly easy to do.

Many of the examples you will see on the internet will start with using JAXB annotated Java POJOs. This works, but I also feel this is a mistake. The ‘contract’ for your web services is now a Java class. Which may work for you, but it’s not portable. What if one of your clients is writing in PHP??? A JAXB annotated Java class is useless to them.

XML Schema

XML Schema is a specification for describing XML documents. Think of it as strong typing for an XML document. You can specify the properties, if they can be null, their data types, if they can be a list, etc. The capabilities of XML schema is very robust.

A really cool feature of JAXB is you can generate JAXB annotated Java POJOs from a XML Schema document. And when you do this, you now have a portable contract for the data types of your web services. Your ‘contract’ is no longer tied to the Java programming language. You could give the XML schema to someone building a client for your web service in another programming language, like C# maybe, and they can use the XML Schema to generate code for the client.

XML Schema is a widely accepted standard that is not tied to a specific programming language. By using XML schema and providing it to your clients you make your Restful Web Service easier to consume. Your clients have a standardised specification of exactly what they need to send.

JAXB Generated Classes for Restful Web Services

In this post, I’m going to show you how to setup a Maven project to create a Jar file of Java classes generated by JAXB from an XML Schema.

Generating a Maven Project in IntelliJ

For the purposes of this example, I’m going to use IntelliJ to create a Maven project. In real use, you’ll want to set this up as either a independent Maven project or a Maven module. This will allow the JAXB generated classes to be bundled in a JAR file and reused in other projects or modules.

1. Create a new project in IntelliJ.

Create Maven Project in IntelliJ

2. Set the GroupId and ArtifactId in the New Project dialog of IntelliJ.

New Project Dialog in IntelliJ

3. Select where to store the project on your drive.

path location in IntelliJ

4. IntelliJ will ask you to verify the creation of a new directory. Click OK.

Directory Does Not Exist dialog in IntelliJ

5. Depending on your settings in IntelliJ, you may be asked to import changes. I often keep this feature off when dealing with large complex Maven projects due to performance reasons. If you see this dialog, click on ‘Import Changes’.

import Maven changes into IntelliJ

6. At this point, you’ve created a new Maven project in IntelliJ. You can see the Maven standard directory structure has been created.

Maven project in IntelliJ

 

Create the XML Schema

The first file we will create is the XML Schema we will be using. Let’s say we’re creating a Web Service to add a product and need a create product command object.

In our XML Schema we are creating a Product class, and the a CreateProductRequest class too. This is going to extend the ProductClass and add a field for the API key. This file is placed in the folder /main/resources. This is the default location for XML Schema files.

jaxb.xsd

<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0">

    <!--This tells JAXB what package to create the Java classes in-->
    <xsd:annotation>
        <xsd:appinfo>
            <jaxb:schemaBindings>
                <jaxb:package name="guru.springframework.domain"/>
            </jaxb:schemaBindings>
        </xsd:appinfo>
    </xsd:annotation>

    <xsd:complexType name="Product">
        <xsd:sequence>
            <xsd:element name="productId" type="xsd:integer"/>
            <xsd:element name="productDescription" type="xsd:string"/>
            <xsd:element name="productPrice" type="xsd:decimal"/>
        </xsd:sequence>
    </xsd:complexType>

    <xsd:complexType name="CreateProductRequest">
        <xsd:complexContent>
            <xsd:extension base="Product">
                <xsd:attribute name="apikey" type="xsd:string"/>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>

</xsd:schema>

Configure Maven

Creating the project in IntelliJ gave us a very basic Maven POM file. Here is the Maven POM created for us.

<?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>guru.springframework</groupId>
    <artifactId>jaxb-xsd-example</artifactId>
    <version>1.0-SNAPSHOT</version>

</project>

Maven Dependencies

We need to add three dependencies to our Maven POM. This first is the JAXB API, the second is the JAXB implementation, and finally the third is for the Maven JAXB plugin.

    <dependencies>
        <!--jaxb support-->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.2.12</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.11</version>
        </dependency>
    </dependencies>

Maven JAXB Plugin

Next we need to configure the JAXB plugin for Maven to the Maven POM.

            <plugin>
                <groupId>org.jvnet.jaxb2.maven2</groupId>
                <artifactId>maven-jaxb2-plugin</artifactId>
                <version>0.12.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

Complete Maven POM

Here is the final Maven POM.

<?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>guru.springframework</groupId>
    <artifactId>jaxb-xsd-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--jaxb support-->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.2.12</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.11</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.jvnet.jaxb2.maven2</groupId>
                <artifactId>maven-jaxb2-plugin</artifactId>
                <version>0.12.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Building Our JAXB Maven Project

Running the Maven Package Goal

IntelliJ makes working with Maven very easy. On the right side of the IDE, you’ll see a button for ‘Maven Projects’, clicking that will open the ‘Maven Projects’ dialog. To build our project, under Lifecycle, double click on the ‘package’ goal.

Maven Projects in IntelliJ

You should see Maven run and build successfully.

 Tests run: 0, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- maven-jar-plugin:2.3.2:jar (default-jar) @ jaxb-xsd-example ---
[INFO] Building jar: /Users/jt/src/springframework.guru/blog/jaxb-xsd-example/target/jaxb-xsd-example-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.410s
[INFO] Finished at: Fri Aug 07 07:23:44 EDT 2015
[INFO] Final Memory: 16M/207M
[INFO] ------------------------------------------------------------------------

Maven Build Artifacts

Maven will build into the ‘Target’ directory. You can see the Java classes generated in IntelliJ.

JAXB Maven Artifacts

JAXB Generated Classes

We expected two classes generated from the XML Schema we defined.

Product.java

//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.11 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2015.08.07 at 07:23:43 AM EDT 
//


package guru.springframework.domain;

import java.math.BigDecimal;
import java.math.BigInteger;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for Product complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="Product"&gt;
 *   &lt;complexContent&gt;
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
 *       &lt;sequence&gt;
 *         &lt;element name="productId" type="{http://www.w3.org/2001/XMLSchema}integer"/&gt;
 *         &lt;element name="productDescription" type="{http://www.w3.org/2001/XMLSchema}string"/&gt;
 *         &lt;element name="productPrice" type="{http://www.w3.org/2001/XMLSchema}decimal"/&gt;
 *       &lt;/sequence&gt;
 *     &lt;/restriction&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Product", propOrder = {
    "productId",
    "productDescription",
    "productPrice"
})
@XmlSeeAlso({
    CreateProductRequest.class
})
public class Product {

    @XmlElement(required = true)
    protected BigInteger productId;
    @XmlElement(required = true)
    protected String productDescription;
    @XmlElement(required = true)
    protected BigDecimal productPrice;

    /**
     * Gets the value of the productId property.
     * 
     * @return
     *     possible object is
     *     {@link BigInteger }
     *     
     */
    public BigInteger getProductId() {
        return productId;
    }

    /**
     * Sets the value of the productId property.
     * 
     * @param value
     *     allowed object is
     *     {@link BigInteger }
     *     
     */
    public void setProductId(BigInteger value) {
        this.productId = value;
    }

    /**
     * Gets the value of the productDescription property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getProductDescription() {
        return productDescription;
    }

    /**
     * Sets the value of the productDescription property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setProductDescription(String value) {
        this.productDescription = value;
    }

    /**
     * Gets the value of the productPrice property.
     * 
     * @return
     *     possible object is
     *     {@link BigDecimal }
     *     
     */
    public BigDecimal getProductPrice() {
        return productPrice;
    }

    /**
     * Sets the value of the productPrice property.
     * 
     * @param value
     *     allowed object is
     *     {@link BigDecimal }
     *     
     */
    public void setProductPrice(BigDecimal value) {
        this.productPrice = value;
    }

}

CreateProductRequest.java

Notice how this class actually extends the Product class.

//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.11 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2015.08.07 at 07:23:43 AM EDT 
//


package guru.springframework.domain;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for CreateProductRequest complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="CreateProductRequest"&gt;
 *   &lt;complexContent&gt;
 *     &lt;extension base="{}Product"&gt;
 *       &lt;attribute name="apikey" type="{http://www.w3.org/2001/XMLSchema}string" /&gt;
 *     &lt;/extension&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "CreateProductRequest")
public class CreateProductRequest
    extends Product
{

    @XmlAttribute(name = "apikey")
    protected String apikey;

    /**
     * Gets the value of the apikey property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getApikey() {
        return apikey;
    }

    /**
     * Sets the value of the apikey property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setApikey(String value) {
        this.apikey = value;
    }

}

Conclusion

This is just a very simple example of generating classes from an XML Schema using JAXB and Maven. In this post, I’ve shown you step by step how to use an XML Schema and JAXB to generate Java classes. The generated classes are bundled into a JAR file, which is portable and can be shared with other Java projects.

As a Spring Source consultant, I was at a large company which built a number of Restful APIs. The team building the APIs did not use JAXB to generate classes from an XML Schema. Rather they built the JAXB classes by hand and could not offer their clients an XML Schema. As a consumer of their APIs, it was a time-consuming process to configure and troubleshoot my data types.

I’ve also done consulting in organizations where the team did use XML Schemas for building their APIs. Having the XML Schema made writing the client code a snap.

Resources

JAXB

You can find the documentation for the JAXB project here.

JAXB Maven Plugin

I used the default settings for the JAXB Maven plugin. Additional options are available. Documentation for the JAXB Maven plugin is here.

Project Source Code

The source code used in this tutorial is available on Github here.

 

About jt

    You May Also Like

    6 comments on “You Should Use JAXB Generated Classes for Restful Web Services

    1. September 10, 2015 at 10:10 am

      Excellent post, I have a question and sorry if it’s quite newb, because I’m new to the Java Web side. So how the clients use this XML that we wrote to generate the client basis code. Would they create their own model based in the data types we have in the XML file? I don’t know if I’m explaining my self correctly I would love If you could give an example what the client part would be for this code let’s say a client use just html, css and javascript. Thanks!

      Reply
      • September 10, 2015 at 10:37 am

        Not sure if I totally follow your question. XML Schema (XSD) is used to define what is valid XML – its a specification of what your application will accept. As long as the client creates an XML document which complies with the XSD, it will validate and process fine.

        Reply
    2. January 4, 2017 at 1:09 am

      Why cant we generate xsd from a jaxb annotated pojos? then we can share the contract with our clients right?

      Reply
    3. September 20, 2017 at 5:41 pm

      Do you nave an example of using RestTemplate to consume a webservice and unmarshall the result to the generated object model?

      Reply
    4. July 19, 2019 at 5:33 pm

      I liked your post, though i have a specific scenario, the XSD is provided to me by a client as wadl file and in that some attributes are upper case, if i refer to you example, its something like

      Now when the java class is generated , the attribute is converted into productId.
      The client API expect the request to have ProductId, though when i build the request object , it will be in camel case. Do you know how preserve attribute case during unmarshalling process.

      Reply
    5. August 9, 2019 at 3:48 pm

      Excellent post !! However, I am trying it on Eclipse, I encounter several missing artifacts in my pom.xml. Any resolutions to this? thanks!

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.