-
Notifications
You must be signed in to change notification settings - Fork 1k
APISerialization
- JAX-RS is the "Java API for RESTful Web Services". It defined @Path, @GET, @PUT, @Produces, @PathParam, @QueryParam etc. We use the JAX-RS reference implementation called Jersey. see http://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services
- JAXB is the "Java Architecture for XML Binding", which maps Java classes to XML representations so they can be marshalled and unmarshalled. see http://en.wikipedia.org/wiki/JAXB
This Stack Overflow response clarifies the relationship: http://stackoverflow.com/a/17981041/778449 The JAX-RS implementation needs to receive and send objects (request and response bodies) over HTTP. When JAX-RS wants to return a response with the application/xml media type, it uses JAXB to figure out how to write objects out as XML.
JAXB entails adding annotations to classes indicating what fields should be serialized and how, such as @XmlRootElement and @XmlElement (actually, the original intent was to generate annotated class files from an XML schema). If we want to also provide other formats such as JSON, the JSON library could re-use the JAXB annotations to provide equivalent serialized responses for the same annotated objects. So the JAXB annotations (despite including the letters XML) can be applied to an XML or JSON serialization process.
However, there are ugly points in using JAXB to produce JSON. For example. the attributes vs. content distinction in XML does not naturally exist in JSON. When going through JAXB to produce JSON, HashMaps will also become strangely verbose. It is therefore often cleaner to simply use a library that converts objects directly to JSON. See: http://stackoverflow.com/questions/6001817/jackson-with-jaxb
It appears that Jersey does not automatically include a JSON MessageWriter, because if you remove the Maven dependency com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider from the POM, then requests for Content-type:appication/json will fail with a:
javax.ws.rs.WebApplicationException: com.sun.jersey.api.MessageException:
A message body writer for Java class org.opentripplanner.api.model.transit.RouteList, and Java type class org.opentripplanner.api.model.transit.RouteList, and MIME media type application/json was not found.
You need a JSON library that implements the extension points MessageBodyReader/Writer of JAX-RS, then declares its implementations to Java's "service loader mechanism", allowing Jersey (and other software) to automatically detect and use it for JSON parsing/writing.
Some wisdom gleaned from Stack Overflow and wikis on choosing and using a JSON library:
- "Jackson and Gson are the most complete Java JSON packages regarding actual data binding support; many other packages only provide primitive Map/List (or equivalent tree model) binding." That is, Jackson and Gson allow marshalling and unmarshalling objects, other libraries may just parse the JSON into a tree of strings.
- Jackson 1.x and 2.x live in different Java packages (1.x under org.codehaus.jackson, 2.x under com.fasterxml.jackson), be sure to import Jackson objects from matching versions.
- Do not use Maven dependencies jersey-media-json (doesn't exist anymore in Jersey 2.x) and jersey-json (only for Jersey 1.x). Use either jersey-media-moxy (for JAXB) or jersey-media-json-jackson (for POJO). The Jersey User Guide has information on these modules in the JSON section at https://jersey.java.net/documentation/latest/media.html#json . Note that in OTP we are using yet another module from Jackson itself, I have no idea what the difference is with the jersey-media module.
- "As of v1.18 of Jersey you do NOT need to write your own MessageBodyWriter and the return type of your @GET methods can be POJO-objects." That is, your Jersey resource methods can just return any old object, and Jackson will still be used to serialize it according to the usual rules.
- "Jackson 1.7 added ability to register serializers and deserializes via Module interface. This is the recommended way to add custom serializers -- all serializers are considered "generic", in that they are used for subtypes unless more specific binding is found. The simplest way is to extend SimpleModule, add serializer(s), and register module with ObjectMapper."
We are using "POJO mapping" and "natural" serialization rather than JAXB. POJO mapping means just converting the public fields and getters of objects to JSON naturally, rather than requiring annotations.
We do need to somehow enable the "POJO mapping feature", which maps public fields and getters of objects to XML/JSON instead of requiring annotations, and "natural mapping" lest we automatically get "Badgerfish" JSON style which perfectly reproduces the semantics of XML but is bizarrely non-idiomatic in JSON.
Jackson will simply include any public field or getter method when producing its JSON representation of an object.
unless you are intentionally working with legacy versions of OpenTripPlanner. Please consult the current documentation at readthedocs