While transforming between Java classes and JSON, Jackson library considers both its own annotations and the conventional JAXB annotations. The final result maybe not obvious. Let's consider a sample class from a sample application:
@XmlRootElement @XmlAccessorType(XmlAccessType.PUBLIC_MEMBER) public class MyBean { String firstName, lastName, fullName; public MyBean(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public MyBean() { } @XmlElement(name = "jaxbFirstName") public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @JsonProperty("jacksonLastName") public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @XmlElement(name = "jaxbFullName") @JsonProperty("jacksonFullName") public String getFullName() { return firstName + " " + lastName; } public void setFullName(String fullName) { this.fullName = fullName; } }
Jackson set up by RESTEasy prefers its own annotations over the JAXB ones. Note, the default object mapper ignores JAXB annotations (see below). The default output of an object mapper will be:
{"jaxbFirstName":"John","jacksonLastName":"Smith","jacksonFullName":"John Smith"}
Configuring Jackson used by JAX-RS
To configure Jackson, one has to provide his own configured instance by means of a context provider implementing
ContextResolver
public class MyObjectMapperProvider implements ContextResolver{ ObjectMapper objectMapper = createObjectMapper(); @Override public ObjectMapper getContext(final Class type) { return objectMapper; } ObjectMapper createObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enable(SerializationFeature.INDENT_OUTPUT); return objectMapper; } }
And the custom provider has to be registered as a singleton:
@ApplicationPath("/api") public class MyApplication extends Application { public MyApplication() { singletons = new HashSet<Object>() { { add(new MyObjectMapperProvider()); } }; resources = new HashSet<Class<?>>() { { add(MyResource.class); } }; } Set<Object> singletons; Set<Class<?>> resources; @Override // note, it is called twice during RESTEasy initialization, public Set<Class<?>> getClasses() { System.out.println(">getClasses()"); return resources; } @Override // note, it is called twice during RESTEasy initialization, public Set<Object> getSingletons() { System.out.println(">getSingletons()"); return singletons; } }
The json received from the service is formatted now:
{ "firstName" : "John", "jacksonLastName" : "Smith", "jacksonFullName" : "John Smith" }
Note, unlike the default Jackson object mapper in RESTEasy, the default Jackson object mapper (created as above ObjectMapper objectMapper = new ObjectMapper() ) does not recognize JAXB annotations.
Enabling JAXB annotations in Jackson object mapper
The customized object mapper instance has to be further configured in the context provider shown above:
ObjectMapper createObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enable(SerializationFeature.INDENT_OUTPUT).registerModule(new JaxbAnnotationModule()); return objectMapper; }
Now JAXB annotations are priveleged over Jackson ones in the produced JSON:
{ "jaxbFirstName" : "John", "jacksonLastName" : "Smith", "jaxbFullName" : "John Smith" }
Disabling unconventional Jackson annotations
The customized object mapper instance has to be further configured in the context provider shown above:
ObjectMapper createObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enable(SerializationFeature.INDENT_OUTPUT).setAnnotationIntrospector(new JaxbAnnotationIntrospector());; return objectMapper; }
Now Jackson are ignored in the produced JSON:
{ "lastName" : "Smith", "jaxbFirstName" : "John", "jaxbFullName" : "John Smith" }
Ignore empty properties during serialization
Another usefull setting feature preventing nulls and empty collections from being included into resulting json.
public class MyObjectMapperProvider implements ContextResolver{ static ObjectMapper objectMapper = createObjectMapper(); @Override public ObjectMapper getContext(final Class type) { return objectMapper; } static ObjectMapper createObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enable(SerializationFeature.INDENT_OUTPUT).setAnnotationIntrospector(new JaxbAnnotationIntrospector()).setSerializationInclusion(JsonInclude.Include.NON_EMPTY); return objectMapper; } public static ObjectMapper getObjectMapper() { return objectMapper; } }
No comments:
Post a Comment