TOTD #8: Generating JSON using JAXB annotations in Jersey

Jersey provides a pluggable type system for the encoding/decoding of a Java type to/from an entity of an HTTP response/request. JSON support was added in Jersey using the BadgerFish convention for encoding/decoding JAXB beans to/from JSON. A new sample was also added in the recently released 0.2.1 that demonstrates this concept.

This TOTD provides provides a trivial sample (using the snippets shown on BadgerFish) that will allow you to experiment with this feature in Jersey. This sample consists of a Resource, Server and "build.xml" to build, deploy, run the server and invoke the endpoint.

Here is the code for the REST resource:

import javax.ws.rs.HttpMethod;
import javax.ws.rs.ProduceMime;
import javax.ws.rs.UriTemplate;

@UriTemplate("/helloworld")
public class HelloWorldResource {

  @HttpMethod
  @ProduceMime("application/xml")
  @UriTemplate("/xml")
  public States getXML() {
    return new alice();
  }

  @HttpMethod
  @ProduceMime("application/json")
  @UriTemplate("/json")
  public States getJSON() {
    return new alice();
  }
}

This resource is published on two URIs - one using XML (/xml) and the other using JSON (/json) representation. Here is the code to start the server:

import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.ws.rest.api.container.ContainerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;

public class HelloWorld {
  public static void main(String[] args) throws IOException {
    HttpHandler handler = ContainerFactory.createContainer(
      HttpHandler.class,
      HelloWorldResource.class);

    HttpServer server = HttpServer.create(new InetSocketAddress(9998), 0);
    server.createContext("/", handler);
    server.setExecutor(null);
    server.start();

    System.out.println("Server running");
    System.out.println("Visit: http://localhost:9998/helloworld");
    System.out.println("Hit return to stop...");
    System.in.read();
    System.out.println("Stopping server");
    server.stop(0);
    System.out.println("Server stopped");
  }
}

This is a very standard code that is available in all the bundled samples. And here is the "build.xml":

<project name="json" default="default" basedir=".">
  <property name="jersey.home" value="c:\\jersey-0.2.1-ea"/>
  <path id="jersey.classpath">
    <fileset dir="${jersey.home}\\lib" includes="\*.jar"/>
  </path>
  <property name="build.dir" value="build"/>
  <property name="src.dir" value="src"/>

  <target name="init">
    <mkdir dir="${build.dir}"/>
  </target>

  <target name="build" depends="init">
    <javac srcdir="${src.dir}" destdir="${build.dir}" includes="\*\*/\*.java">
      <classpath refid="jersey.classpath"/>
    </javac>
  </target>

  <target name="run" depends="build">
    <java classname="samples.HelloWorld" fork="true">
      <classpath>
        <path refid="jersey.classpath"/>
        <pathelement location="${build.dir}"/>
      </classpath>
    </java>
  </target>

  <target name="get-json">
    <get src="http://localhost:9998/helloworld/json" dest="out.json"/>
  </target>

  <target name="get-xml">
    <get src="http://localhost:9998/helloworld/xml" dest="out.xml"/>
  </target>

  <target name="clean">
    <delete quiet="true" dir="${build.dir}"/>
  </target>
</project>

Make sure to set the value of "jersey.home" property correctly in this file. And now the JAXB bean and corresponding XML and JSON representation:

JAXB Bean XML representation JSON representation
@javax.xml.bind.annotation.XmlRootElement
public class alice {

  @javax.xml.bind.annotation.XmlValue
  protected String value;

  public alice() { this.value = "bob" }
  public String getValue() { return value; }
}

<alice>bob</alice>

{"alice":{"$":"bob"}}
@javax.xml.bind.annotation.XmlRootElement
public class alice {

  @javax.xml.bind.annotation.XmlElement
  protected String bob;

  @javax.xml.bind.annotation.XmlElement
  protected String david;

  public alice() {
    bob = "charlie";
    david = "edgar";
  }

  public String getBob() { return bob; }
  public String getDavid() { return david; }
}
<alice>
  <bob>charlie</bob>
  <david>edgar</david>
</alice>
{"alice":{"bob":{"$":"charlie"},"david":{"$":"edgar"}}}
@javax.xml.bind.annotation.XmlRootElement
public class alice {
  @javax.xml.bind.annotation.XmlValue
  protected String value;

  @javax.xml.bind.annotation.XmlAttribute
  protected String charlie;

  public alice() {
    value = "bob";
    charlie = "david";
  }

  public String getValue() { return value; }
  public String getCharlie() { return charlie; }
}
<alice charlie="david">
  bob
</alice>
{"alice":{"@charlie":"david","$":"bob"}}
@javax.xml.bind.annotation.XmlRootElement(namespace="http://some-namespace")
public class alice {
  @javax.xml.bind.annotation.XmlValue
  protected String value;

  public alice() { value = "bob"; }

  public String getValue() { return value; }
}
<alice xmlns="http://some
-namespace">
  bob
</alice>
{"alice":{"@xmlns":{"$":"http:\\/\\/some-namespace"},"$":"bob"}}
@javax.xml.bind.annotation.XmlRootElement
public class alice {
  @javax.xml.bind.annotation.XmlElement
  protected java.util.List<String> bob;

  public alice() {
    bob = new java.util.ArrayList<String>();
    bob.add("charlie");
    bob.add("david");
  }

  public java.util.List<String> getBob() { return bob; }
}
<alice>
  <bob>charlie</bob>
  <bob>david</bob>
</alice>
{"alice":{"bob":[{"$":"charlie"},{"$":"david"}]}}

JSON representation can always be constructed using the JSON APIs as shown below and by adding the method to "HelloWorldResource":

import org.codehaus.jettison.json.\*;

@HttpMethod("GET")
@ProduceMime("application/json")
@UriTemplate("/json2")
public JSONObject getJSONMessage() throws JSONException {
  JSONObject object = new JSONObject();
  JSONObject content = new JSONObject();
  content.put("$", "bob");
  object.put("alice", content);

  return object;
}

JAX-WS also supports JSON as a pluggable encoding.

Please leave suggestions on other TOTD that you'd like to see. A complete archive is available here.

Technorati: totd jersey json jax-ws REST pluggableencoding restful

Comments:

[Trackback] Jersey is the open source, production quality, JAX-RS (JSR 311) Reference Implementation for building RESTful Web services in the GlassFish community. It also provides an API that allows developers to extend Jersey to suite their requirements. This Ti...

Posted by Arun Gupta's Blog on November 24, 2008 at 10:39 PM PST #

I haven been struggling fixing this import statement

import javax.ws.rs.ProduceMime;

Do you know what jar files I need for that?

Thanks

David

Posted by David on January 08, 2009 at 03:44 AM PST #

Multiple ways:

- In NetBeans, include JAX-RS 1.0 library using Library Manager
- In GlassFish, use Update Center and pull the jars (http://blogs.sun.com/japod/entry/jersey_0_2_1_available)
- Download JARs from jersey.dev.java.net and include explicitly

Posted by Arun Gupta on January 08, 2009 at 08:07 AM PST #

Hi!
Does your first JAXB example really work?!
I cannot use XmlElement AND XmlValue at the same time...

com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
If a class has @XmlElement property, it cannot have @XmlValue property.

Posted by Christoph on September 17, 2009 at 09:19 PM PDT #

Christoph,

The first example uses XmlRootElement and XmlValue, the 2nd example is XmlElement. Does that clarify ?

Posted by Arun Gupta on September 17, 2009 at 11:08 PM PDT #

Does it deal with multiple arrays in an object correctly? Say that you have

@XmlRootElement
class Foo {
@Element
Collection<Bar> bars;
@Element
Collection<Qux> quxen;
}

If each of those collections contain two elements, such that the XML would be

<foo>
<bar>Tango Bar</bar>
<bar>Limbo Bar</bar>
<qux>Quxote</qux>
<qux>Of the matter</qux>
</foo>

will the resulting JSON be

{
foo: {
bar: [{"$": "Tango Bar"}, {"$": "Limbo Bar"}],
qux: [{"$": "Quxote"}, {"$": "Of the matter"}]
}
}

Not in my experience. Since the arrays are flattened in XML, it becomes

{
foo: {
bar: [{"$": "Tango Bar"}, {"$": "Limbo Bar"}, {"qux": {"$": "Quxote"}}, {"qux": {"$": "Of the matter"}}]
}
}

The elements from the second collection get included in the first collection. Bug, or simple consequence of going to XML first? You be the judge.

Posted by Lars Clausen on February 22, 2010 at 06:11 PM PST #

Nice Work Arun. Helped me a lot while I was working on my REST Based Web Service project recently. Can you post article on generating JSON using JAXB annotations in Apache Wink JAX-RS specification?

Posted by guest on May 14, 2011 at 06:40 AM PDT #

guest, Glad you liked the blog. I've never used Wink so can't help much there but you should be able to ask this question on their user forum.

Posted by Arun Gupta on May 16, 2011 at 02:41 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today