Recently I posted how to leverage EclipseLink JAXB (MOXy)'s JSON binding to create a RESTful service. In this post I will demonstrate how easy it is to take advantage of MOXy's JSON binding on the client side.
- MOXy as Your JAX-RS JSON Provider - Server Side
- MOXy as Your JAX-RS JSON Provider - Client Side
- MOXy as Your JAX-RS JSON Provider - MOXyJsonProvider
URI
This post will focus on the following URI from the service we declared in the previous post. The following call will return a list of customers that live in "Any Town".
http://localhost:8080/CustomerService/rest/customers/findCustomersByCity/Any%20Town
Java SE Client APIs
In the first example we will use the standard Java SE 6 APIs. Some interesting items to note:
- MOXy can directly marshal (line 35) and unmarshal (line 28) collections to/from JSON arrays without requiring a wrapper object.
- There are no compile time dependencies on MOXy (it is a run time dependency).
- The eclipselink.media-type property is used to enable JSON binding on the unmarshaller (line 25) and marshaller (line 33).
- The eclipselink.json.include-root property is used to indicate that the @XmlRootElement annotation should be ignored in the JSON binding (lines 26 and 34).
package example;
import java.io.InputStream;
import java.net.*;
import java.util.List;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.example.Customer;
public class JavaSEClient {
private static final String MEDIA_TYPE = "application/json";
public static void main(String[] args) throws Exception {
String uri = "http://localhost:8080/CustomerService/rest/customers/findCustomersByCity/Any%20Town";
URL url = new URL(uri);
HttpURLConnection connection =
(HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", MEDIA_TYPE);
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty("eclipselink.media-type", MEDIA_TYPE);
unmarshaller.setProperty("eclipselink.json.include-root", false);
InputStream xml = connection.getInputStream();
List<Customer> customers = (List<Customer>) unmarshaller.unmarshal(new StreamSource(xml), Customer.class).getValue();
connection.disconnect();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", MEDIA_TYPE);
marshaller.setProperty("eclipselink.json.include-root", false);
marshaller.marshal(customers, System.out);
}
}
Output
Below is the output from running the Java SE client. For those that may have used a JAXB (JSR-222) implementation with something like Jettison to produce/consume JSON, the following are some interesting items to note:
Below is the output from running the Java SE client. For those that may have used a JAXB (JSR-222) implementation with something like Jettison to produce/consume JSON, the following are some interesting items to note:
- MOXy renders collections as JSON arrays.
- MOXy represents the numeric values correctly without quotes (line 26).
- MOXy surrounds collections of size 1 correctly with square brackets (lines 28 and 32).
[ {
"address" : {
"city" : "Any Town",
"id" : 1,
"street" : "1 A Street"
},
"firstName" : "Jane",
"id" : 1,
"lastName" : "Doe",
"phoneNumbers" : [ {
"id" : 2,
"num" : "555-2222",
"type" : "HOME"
}, {
"id" : 1,
"num" : "555-1111",
"type" : "WORK"
} ]
}, {
"address" : {
"city" : "Any Town",
"id" : 10,
"street" : "456 Another Road"
},
"firstName" : "Sue",
"id" : 10,
"lastName" : "Jones",
"phoneNumbers" : [ {
"id" : 10,
"num" : "555-3333",
"type" : "WORK"
} ]
} ]
Jersey Client APIs
JAX-RS 2.0 (JSR-339) is working on standardizing the client APIs. With JAX-RS 1.0 many of the implementations provide their own version. Below is an example using the client APIs provided by Jersey. Note how we can leverage the exact same MessageBodyReader/Writer that we used on the server side (line 14, refer to MOXy as Your JAX-RS JSON Provider - Server Side). I have also specified the LoggingFilter (line 17) so we can take a closer look at the message.
package example;
import java.util.List;
import org.example.Customer;
import org.example.CustomerJsonProvider;
import com.sun.jersey.api.client.*;
import com.sun.jersey.api.client.config.*;
import com.sun.jersey.api.client.filter.LoggingFilter;
public class JerseyClient {
public static void main(String[] args) {
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(CustomerProvider.class);
Client client = Client.create(cc);
client.addFilter(new LoggingFilter());
WebResource resource = client.resource("http://localhost:8080/CustomerService/rest/customers");
List<Customer> customers = resource.path("/findCustomersByCity/Any%20Town").accept("application/json").get(new GenericType<List<Customer>>(){});
for(Customer customer : customers) {
System.out.print(customer.getLastName());
System.out.print(", ");
System.out.println(customer.getFirstName());
}
}
}
Alternatively you could use MOXyJsonProvider which is an implementation of MessageBodyReader/MessageBodyWriter that is provided by EclipseLink.
package example;
import java.util.List;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
import org.example.Customer;
import com.sun.jersey.api.client.*;
import com.sun.jersey.api.client.config.*;
import com.sun.jersey.api.client.filter.LoggingFilter;
public class JerseyClient {
public static void main(String[] args) {
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(MOXyJsonProvider.class);
Client client = Client.create(cc);
client.addFilter(new LoggingFilter());
WebResource resource = client.resource("http://localhost:8080/CustomerWAR/rest/customers");
List<Customer> customers = resource.path("/findCustomersByCity/Any%20Town").accept("application/json").get(new GenericType<List<Customer>>(){});
for(Customer customer : customers) {
System.out.print(customer.getLastName());
System.out.print(", ");
System.out.println(customer.getFirstName());
}
}
}
Output
Below is the output from running the Jersey client.
14-Mar-2012 4:08:12 PM com.sun.jersey.api.client.filter.LoggingFilter log
INFO: 1 * Client out-bound request
1 > GET http://localhost:8080/CustomerService/rest/customers/findCustomersByCity/Any%20Town
1 > Accept: application/json
1 >
14-Mar-2012 4:08:12 PM com.sun.jersey.api.client.filter.LoggingFilter log
INFO: 1 * Client in-bound response
1 < 200
1 < Transfer-Encoding: chunked
1 < Date: Wed, 14 Mar 2012 20:08:12 GMT
1 < Content-Type: application/json
1 < X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.1 Java/Oracle Corporation/1.7)
1 < Server: GlassFish Server Open Source Edition 3.1.1
1 <
[{"address" : {"city" : "Any Town", "id" : 1, "street" : "1 A Street"}, "firstName" : "Jane", "id" : 1, "lastName" : "Doe", "phoneNumbers" : [{"id" : 1, "num" : "555-1111", "type" : "WORK"}, {"id" : 2, "num" : "555-2222", "type" : "HOME"}]}, {"address" : {"city" : "Any Town", "id" : 10, "street" : "456 Another Road"}, "firstName" : "Sue", "id" : 10, "lastName" : "Jones", "phoneNumbers" : [{"id" : 10, "num" : "555-3333", "type" : "WORK"}]}]
Doe, Jane
Jones, Sue
Further Reading
If you enjoyed this post then you may also be interested in:
- RESTful Services
- MOXy as Your JAX-RS JSON Provider - MOXyJsonProvider
- MOXy as Your JAX-RS JSON Provider - Server Side
- Creating a RESTful Service
- Part 2 - Mapping the Database to JPA Entities
- MOXy's XML Metadata in a JAX-RS Service
- JSON Binding
Hi,
ReplyDeleteI am using moxy as the jaxb provider in my application.When i am marshalling an JPA entity
it ends up by populating all the attributes even lazy initialized ones.But as per my requirement only non-lazy attributes should be populated.Is it a bug in moxy or normal behavior.
Hi,
DeleteI apologize for the delay in responding, the EclipseLink 2.4.0 release is rapidly approaching and has been my primary focus lately. By default a JAXB (JSR-222) implementation will use the public accessors, this will trigger the lazy relationships set up by the JPA implementation. For your use case I would recommend using JAXB with @XmlAccessorType(XmlAccessType.FIELD). The field should be null if it has not been realized yet, and by default will not be marshalled.
- Using JAXB's @XmlAccessorType to Configure Field or Property Access
- Binding to JSON & XML - Handling Null
-Blaise
Very Good Example of REST and JSON
ReplyDeleteThanks for this. I was using Jettison + JDK's JAXB and Jettison won't ignore the root element. MOXy's ability to ignore the root element when generating JSON is most welcome. Didn't want a nested document when inserting into ElasticSearch.
ReplyDelete