MOXy is the New Default JSON-Binding Provider in GlassFish 4
Join the DZone community and get the full member experience.
Join For FreeGlassFish 4 is now available offering the complete Java EE 7 (JSR-342) platform. EclipseLink made some major contributions to this release. The first is providing the JPA 2.1 (JSR-338) implementation. The second which I'll cover in this post is EclipseLink MOXy is now the default JSON-binding provider for JAX-RS applications.
RESTful Service
Normally a real service will be backed by JPA to do persistence operations (see: Creating a RESTful Web Service - Part 4/5). But for this post I will use a "Hello World" style service that returns a Customer based on an ID as XML and JSON to illustrate some points about binding.
package org.example.service; import javax.ejb.*; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import org.example.model.*; @Stateless @LocalBean @Path("/customers") public class CustomerService { @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("{id}") public Customer read(@PathParam("id") int id) { Customer customer = new Customer(); customer.setId(id); customer.setName("Jane Doe"); PhoneNumber pn = new PhoneNumber(); pn.setType("work"); pn.setValue("5551111"); customer.getPhoneNumbers().add(pn); return customer; } }
package org.example.service; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("rest/*") public class CustomerApplication extends Application { }
Below is the Java model we will use for this example. The goal is to produce good XML and JSON representations using a single set of metadata.
- The id property is an int. Since JSON has a different representation for numbers and text we will look at how this value gets represented in the JSON output.
- The phoneNumbers property is of type List<PhoneNumber>. It has been annotated with @XmlElementWrapperto produce good XML output. We will examine the impact of doing this on the JSON output.
package org.example.model; import java.util.*; import javax.xml.bind.annotation.*; @XmlRootElement public class Customer { private int id; private String name; private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>(); public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElementWrapper @XmlElement(name="phoneNumber") public List<PhoneNumber> getPhoneNumbers() { return phoneNumbers; } }
In XML the PhoneNumber class maps to a complex type with simple content (see: JAXB and Complex Types with Simple Content). This means it maps to an XML element with attributes and text. These XML concepts do not correspond directly to JSON concepts so we will examine the JSON representation.
package org.example.model; import javax.xml.bind.annotation.*; public class PhoneNumber { private String type; private String value; @XmlAttribute public String getType() { return type; } public void setType(String type) { this.type = type; } @XmlValue public String getValue() { return value; } public void setValue(String value) { this.value = value; } }
Request - GET
Below is the URL we will use to access our service:
http://localhost:8080/CustomerResource/rest/customers/1
Response (application/xml)
Since we mapped our domain model with JAXB (JSR-222) metadata the XML representation below isn't much of a surprise.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <customer id="1"> <name>Jane Doe</name> <phoneNumbers> <phoneNumber type="work">5551111</phoneNumber> </phoneNumbers> </customer>
Response (application/json) - GlassFish 3.1.2
Lets take a look at what the JSON representation looked like back in GlassFish 3.1.2 before MOXy was the default JSON-binding provider. There are several things to take note of:
- The int value for the id property is represented as JSON text.
- The phoneNumbers property of type List<PhoneNumber> since it had a single item was represented as a JSON object instead of a JSON array. Also even when this key contains a collection it is still called phoneNumber.
- The JSON key corresponding to the type property since it was annotated with @XmlAttribute is prefixed with @.
- The JSON key corresponding to the value property since it was annotated with @XmlValue is called $.
{ "id": "1", "name": "Jane Doe", "phoneNumbers": { "phoneNumber": { "@type": "work", "$": "5551111" } } }
Using the default configuration you will see that upgrading to GlassFish 4 eliminates the two biggest problems:
- Numeric Values - Now the id property is correctly marshalled to JSON as a numeric value. This isn't a trick, MOXy bases the JSON representation on the Java type of the property. The value property of type String on the instance of PhoneNumber contained only digits and it is correctly marshalled to JSON as text.
- Collections of Size 1 - The phoneNumber key is now a JSON array, unfortunately it is still called phoneNumber(I'm demonstrate how to fix this next).
{ "id": 1, "name": "Jane Doe", "phoneNumbers": { "phoneNumber": [ "@type": "work", "$": "5551111" ] } }
MOXy can now be configured by leveraging the JAX-RS ContextResolver mechanism. You simply need to return an instance of MOXyJsonConfig. We will use it to do the following:
- Specify that we don't want to prefix the JSON keys that correspond to properties mapped with @XmlAttribute.
- Use the JSON key value instead of $ for properties mapped with @XmlValue.
- MOXyJsonConfig can also be used to pass properties down to the Marshaller/Unmarshaller. We will do this to clean up the JSON key for collection properties (see: Binding to JSON & XML - Handling Collections).
package org.example.service; import javax.ws.rs.ext.*; import org.eclipse.persistence.jaxb.JAXBContextProperties; import org.glassfish.jersey.moxy.json.MoxyJsonConfig; @Provider public class MOXyJsonContextResolver implements ContextResolver<MoxyJsonConfig> { private final MoxyJsonConfig config; public MOXyJsonContextResolver() { config = new MoxyJsonConfig() .setAttributePrefix("") .setValueWrapper("value") .property(JAXBContextProperties.JSON_WRAPPER_AS_ARRAY_NAME, true); } @Override public MoxyJsonConfig getContext(Class<?> objectType) { return config; } }
Finally we have a JSON representation that we can be happy with. It does not contain any XML related artifacts, even though it was produced with the same metadata as the XML representation (which hasn't changed). All it took was a little MOXy.
{ "id": 1, "name": "Jane Doe", "phoneNumbers": [ { "type": "work", "value": "5551111" } ] }
- Jersey Documentation: Section 8.1.2 - MOXy
- EclipseLink 2.5 Release Available for Download
- MOXy as your JAX-RS JSON Provider - MOXyJsonProvider
- Introducing EclipseLink JPA-RS
- GlassFish 3.1.2 is Full of MOXy (EclipseLink JAXB)
Published at DZone with permission of Blaise Doughan, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Structured Logging
-
Getting Started With the YugabyteDB Managed REST API
-
How To Use Pandas and Matplotlib To Perform EDA In Python
-
Transactional Outbox Patterns Step by Step With Spring and Kotlin
Comments