17.4. JAXB and JSON provider
RESTEasy lets you marshal JAXB annotated POJOs to and from JSON with the Jettison JSON library. You can find more information about Jettison at http://jettison.codehaus.org/.
Jettison has two mapping formats: the default Jettison Mapped Convention format, and BadgerFish.
For example, consider this JAXB class:
@XmlRootElement(name = "book") public class Book { private String author; private String ISBN; private String title; public Book() { } public Book(String author, String ISBN, String title) { this.author = author; this.ISBN = ISBN; this.title = title; } @XmlElement public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } @XmlElement public String getISBN() { return ISBN; } public void setISBN(String ISBN) { this.ISBN = ISBN; } @XmlAttribute public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
The JAXB
Book
class would be marshaled to JSON using the BadgerFish Convention:
{"book": { "@title":"EJB 3.0", "author":{"$":"Bill Burke"}, "ISBN":{"$":"596529260"} } }
Element values are associated with a map. To find the value of the element, you must access the
$
variable. You could access the book like this, in JavaScript:
var data = eval("(" + xhr.responseText + ")"); document.getElementById("zone").innerHTML = data.book.@title; document.getElementById("zone").innerHTML += data.book.author.$;
To use the BadgerFish Convention you must use the
@org.jboss.resteasy.annotations.providers.jaxb.json.BadgerFish
annotation either on the JAXB class you are marshaling or unmarshaling, or on the JAX-RS resource method or parameter:
@BadgerFish @XmlRootElement(name = "book") public class Book {...}
To return a
book
on the JAX-RS method without polluting your JAXB classes with RESTEasy annotations, you can add the annotation to the JAX-RS method instead:
@BadgerFish @GET public Book getBook(...) {...}
If your input is a
Book
, place it on the parameter:
@POST public void newBook(@BadgerFish Book book) {...}
The default Jettison Mapped Convention returns the following JSON:
{ "book" : { "@title":"EJB 3.0", "author":"Bill Burke", "ISBN":596529260 } }
Note that
title
is prefixed with the @
character. Unlike the BadgerFish convention, this does not represent the value of element text, which makes it simpler (and a sensible default). To access this in JavaScript:
var data = eval("(" + xhr.responseText + ")"); document.getElementById("zone").innerHTML = data.book.@title; document.getElementById("zone").innerHTML += data.book.author;
The Mapped Convention lets you adjust the JAXB mapping with the
@org.jboss.resteasy.annotations.providers.jaxb.json.Mapped
annotation. With this, you can provide an XML namespace to JSON namespace mapping. For example, if you define your JAXB namespace within your package-info.java
class like so:
@javax.xml.bind.annotation.XmlSchema(namespace="http://jboss.org/books") package org.jboss.resteasy.test.books;
You must define a JSON-to-XML namespace mapping, or you will receive an exception:
java.lang.IllegalStateException: Invalid JSON namespace: http://jboss.org/books at org.codehaus.jettison.mapped.MappedNamespaceConvention .getJSONNamespace(MappedNamespaceConvention.java:151) at org.codehaus.jettison.mapped.MappedNamespaceConvention .createKey(MappedNamespaceConvention.java:158) at org.codehaus.jettison.mapped.MappedXMLStreamWriter .writeStartElement(MappedXMLStreamWriter.java:241)
The
@Mapped
annotation fixes this problem. Place the @Mapped
annotation on your JAXB classes, your JAX-RS resource method, or on the parameter that you are unmarshaling.
import org.jboss.resteasy.annotations.providers.jaxb.json.Mapped; import org.jboss.resteasy.annotations.providers.jaxb.json.XmlNsMap; ... @GET @Produces("application/json") @Mapped(namespaceMap = { @XmlNsMap(namespace = "http://jboss.org/books", jsonName = "books") }) public Book get() {...}
You can also force
@XmlAttribute
s to be marshaled as XMLElements
.
@Mapped(attributeAsElements={"title"}) @XmlRootElement(name = "book") public class Book {...}
To return a
book
on the JAX-RS method without polluting your JAXB classes with RESTEasy annotations, add the annotation to the JAX-RS method:
@Mapped(attributeAsElements={"title"}) @GET public Book getBook(...) {...}
If your input is a
Book
, place it on the parameter:
@POST public void newBook(@Mapped(attributeAsElements={"title"}) Book book) {...}