I am building a REST service application with Spring and Jersey.
The "Response Content Type" can be either JSON or XML, which is why I use JAXB annotations on the fields of my POJO, which works great so far. No configuration was needed to get this to work, I just put the required .jars on the classpath.
Now, I want to add custom behaviour for (de-)serialization of Java 8 Date/Time objects and register the JavaTimeModule from Jackson.
Where would be the right place to register the Module? I know the code snippet is supposed to look something like this:
ObjectMapper tmpMapper = new ObjectMapper();
tmpMapper.registerModule(new JavaTimeModule());
But where do I actually put this code?
The place that comes to mind is my ResourceConfig class from Jersey. It looks like this:
#ApplicationPath("/rest")
public class ApplicationResourceConfig extends ResourceConfig {
public ApplicationResourceConfig() {
// Not needed because since jersey 2.9 JacksonFeature implements AutoDiscoverable
// register(JacksonFeature.class);
register(WadlFeature.class);
// Registers our own request listener for monitoring purposes
register(RestServiceApplicationEventListener.class);
// Registers the package that contains our REST resources
packages(PingResource.class.getPackage().getName());
}
What I did now was to add the following lines at the bottom of the constructor:
ObjectMapper tmpMapper = new ObjectMapper();
tmpMapper.registerModule(new JavaTimeModule());
JacksonJaxbJsonProvider tmpProvider = new JacksonJaxbJsonProvider();
tmpProvider.setMapper(tmpMapper);
register(tmpProvider);
The DateTime conversion now worked. However, some of the the JAXB behaviour changed which caused the deserialization of other beans to break. Here is what is not working anymore:
I have super class which defines a getter of a field:
public abstract class SuperClass {
#XmlElement(name = "yourField")
public abstract String getMyField();
}
(This maps the internal name of the field to different name which is exposed to the outside)
public class SubClass {
private String myField;
#Override
public String getMyField() {
return myField;
}
}
This mechanism used to work before and I could e.g. define a POST parameter of type SubClass calling the method with a JSON snippet that looks like this:
{
"yourField" : "hello world"
}
When trying this now I get the exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "yourField" (class com.myProject.SubClass), not marked as ignorable (1 known property: "myField"])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#728b1889; line: 4, column: 14] (through reference chain: com.myProject.Subclass["yourField"])
Note however, that the basic JAXB features still work, only in this particular superclass/subclass example.
Could it be that explicitly registering a JacksonJaxbJsonProvider somehow altered the default behaviour on how to deal with JAXB annotated beans? Is there a way to maybe retrieve the existing JacksonJaxbJsonProvider (that must have been registered implicitly somehow) or the existing ObjectMapper object and register the JavaTimeModule to it instead of creating a new Mapper object and a new Provider object?
Jackson serialization to JSON has some nice features like SerializationFeature.WRITE_NULL_MAP_VALUES which allows you to avoid writing nulls into your serialized object.
However, if I use a custom JsonSerializer this setting is ignored and I'm forced to put null checks in all my calls to writeStringField. For example, I register a custom JsonSerializer:
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(CloudDocument.class, new ImportProductSerializer());
objectMapper.registerModule(simpleModule);
and then inside of ImportProductSerializer I'm stuck with putting a null check for every field I want to serialize which gets ugly and tedious:
private class ImportProductSerializer extends JsonSerializer<CloudDocument> {
#Override
public void serialize(CloudDocument value,
JsonGenerator gen, //Customize this?
SerializerProvider serializers) throws Exception {
gen.writeStartObject();
gen.writeStringField("type", "add");
//Null check here and every other field: gross!
if(StringUtils.hasText(importProduct.getExtraListingSku1())) {
gen.writeStringField("extralistingsku1", importProduct.getExtraListingSku1());
}
gen.writeEndObject();
}
}
The solution I would like to employ is having my custom JsonSerializer use a custom JsonGenerator. The serialize method takes JsonGenerator in as a parameter and I could easily define it like so:
private class CustomGenerator extends JsonGenerator {
#Override
public JsonGenerator setCodec(ObjectCodec oc) {
super.setCodec(oc);
}
#Override
public void writeStringField(String fieldName, String value) throws IOException {
if(value==null) return;
super.writeStringField(fieldName.toLowerCase(), value);
}
.... //other methods implemented
}
but I'm not sure how to configure the ObjectMapper to use my implementation of CustomGenerator. The benefit of this approach is I could customize the writeField methods to do the null checks and to make sure that the field names subscribe to the naming conventions that are expected which, in this case, involve converting camelCase to lower_underscore.
Is there a way to configure the JsonGenerator that is instantiated with the call to objectMapper.writeValue()?
The question is bit open-ended, as I do not know exactly what you have tried so far; how much of the work is done by custom serializer.
In general serializers are expected to delegate much of the handling to minimize amount of work they do.
But if you have defined a custom serializer to handle POJOs with included fields (instead of a custom serializer for something serialized as a JSON String, or other scalar values), yes, you do need to handle your own checks. This is partly because there are many ways to potentially change inclusion; not just SerializationFeature, but also #JsonFormat annotation for properties; there is no way to handle this transparently when delegating control to custom serializers.
So, yes, if you end up using writeStringField(), you do need to check for default inclusion settings and omit write appropriately.
I am using Jackson 2.3.2 library in a Spring MVC project and trying to specify a custom JSON serialization on a custom wrapper Object by implementing the JSONSerializableWithType interface which includes a method called serialize() that Jackson calls, when trying to serialize an Object instance to JSON.
Strangely this serialize method is called correctly as long as my Object wrapper does not extend LinkedHashMap<...>.
If my Object wrapper extends a Class that Jackson "knows" how to serialize in a default way, the serialize() method is not called anymore.
My wrapper class looks like this:
public class ResponseRoomOccupancy
extends LinkedHashMap<...>
implements org.codehaus.jackson.map.JsonSerializableWithType {
#Override
public void serialize(JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
// actual serialization of the object
jgen.writeEndObject();
}
#Override
public void serializeWithType(JsonGenerator jgen,
SerializerProvider provider, TypeSerializer typeSer)
throws IOException, JsonProcessingException {
serialize(jgen, provider);
}
}
The controller:
#RequestMapping(value = "/occupancy")
public #ResponseBody ResponseRoomOccupancy
getRoomOccupancy(RequestRoomOccupancy request) {
return appointmentService.getEnrichedRoomOccupancy(request);
}
Can anybody explain to me why Jackson does not use the custom-defined serialize method on a class that extends LinkedHashMap?
UPDATE:
As Sotirios Delimanolis pointed out correctly, the JSONSerializableWithType interface is part of an older version of Jackson (<1.9).
However I dont know why this kind of custom serialization works with custom defined classes (e.g. if LinkedHashMap is exchanged with some class Foo<...>).
You're using Jackson 2 which is completely incompatible with Jackson 1. JsonSerializableWithType is an interface from Jackson 1. You can't have them work together. Jackson 2 simply doesn't look for JsonSerializableWithType.
Instead, annotate your LinkedHashMap type with
#JsonSerialize(using = YourSerializer.class)
and have YourSerializer do the work.
Regarding your comment and edit, Jackson has some default serializers/deserializers for known types, like List, Set, String, Number, and Map. However, it does not know your custom types. It must build a new serializer based on what it finds from analyzing your type.
I am using the Jackson library to write custom serializers, and register them inside of a custom ObjectMapper. However, I also want to change the default serialization to simply output the string representation of the object when a more specific custom serialization has not been written.
For example, say I have written custom serializers for classes "Map" and "Entry", in addition to the default serializer. Then the serialization module within my custom ObjectMapper might look like this:
SimpleModule module = new SimpleModule("module", new Version(0, 1, 0, "alpha", null, null));
module.addSerializer(Entry.class, new EntryJsonSerializer());
module.addSerializer(Map.class, new MapJsonSerializer());
module.addSerializer(Object.class, new DefaultJsonSerializer());
this.registerModule(module);
However, I'm finding that the module will use DefaultJsonSerializer to serialize Map and Entry objects (as they are also Object objects).
How can I change the default serialization behavior, while ensuring Entry and Map objects are serialized as intended?
The problem is probably that the actual type of values (say, String) is used for locating serializers.
One solution would be to register serializers for value types, if you know them.
Alternatively, you could force use of static typing; this would make serializer lookup use declared (static) type, not actual runtime type.
This can be done with:
objectMapper.enable(MapperFeature.USE_STATIC_TYPING);
I've gotten around the problem by writing a single serializer and using a series of if statements to implement prioritization:
public final class UnifiedJsonSerializer extends JsonSerializer<Object> {
#Override
public void serialize(Object object, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
if (object instanceof Entry) {
// Entry serialization code
} else if (object instanceof Map) {
// Map serialization code
} else {
// default serialization code
}
}
Suppose I have a pojo:
import org.codehaus.jackson.map.*;
public class MyPojo {
int id;
public int getId()
{ return this.id; }
public void setId(int id)
{ this.id = id; }
public static void main(String[] args) throws Exception {
MyPojo mp = new MyPojo();
mp.setId(4);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
System.out.println(mapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.WRAP_ROOT_VALUE));
System.out.println(mapper.writeValueAsString(mp));
}
}
When I serialize using the Jackson ObjectMapper, I just get
true
{"id":4}
but I want
true
{"MyPojo":{"id":4}}
I've searched all over, Jacksons documentation is really unorganized and mostly out of date.
By adding the jackson annotation #JsonTypeInfo in class level you can have the expected output. i just added no-changes in your class.
package com.test.jackson;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
#JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME)
public class MyPojo {
// Remain same as you have
}
output:
{
"MyPojo": {
"id": 4
}
}
I'm not using jackson, but searching I found this configuration that seems to be what you want: WRAP_ROOT_VALUE
Feature that can be enabled to make root value (usually JSON Object but can be any type) wrapped within a single property JSON object, where key as the "root name", as determined by annotation introspector (esp. for JAXB that uses #XmlRootElement.name) or fallback (non-qualified class name). Feature is mostly intended for JAXB compatibility.
Default setting is false, meaning root
value is not wrapped.
So that you can configure mapper:
objectMapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
I hope it helps you...
Below is a way to achieve this
Map<String, MyPojo> singletonMap = Collections.singletonMap("mypojo", mp);
System.out.println(mapper.writeValueAsString(singletonMap));
Output
{ "mypojo" : { "id" : 4}}
Here the advantage is that we can give our on name for the root key of json object. By the above code, mypojo will be the root key. This approach will be most useful when we use java script template like Mustache.js for iteration of json objects
To achieve this you need to use the JsonTypeInfo annotation on your class and in particular WRAPPER_OBJECT
#JsonTypeName("foo")
#JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT ,use = JsonTypeInfo.Id.NAME)
public class Bar(){
)
There is also a nice annotation for this:
#JsonRootName(value = "my_pojo")
public class MyPojo{
...
}
will generate:
{
"my_pojo" : {...}
}
How about simplest possible solution; just use a wrapper class like:
class Wrapper {
public MyPojo MyPojo;
}
and wrapping/unwrapping in your code?
Beyond this, it would help to know WHY you would like additional json object entry like this? I know this is done by libs that emulate json via xml api (because of impedance between xml and json, due to conversion from xml to json), but for pure json solutions it is usually not needed.
Is it to allow you do figure out what actual type is?
If so, perhaps you could consider enabled polymorphic type information, to let Jackson handle it automatically? (see 1.5 release notes, entry for PTH, for details).
there is another way i used and that worked for me.
I am working with a third party jar, so i have no control for annotations.
So i had to write through bit of hack.
Override: org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanProperties(SerializationConfig, BasicBeanDescription)
Add your property as below
List<BeanPropertyWriter> props = super.findBeanProperties(config, beanDesc);
BeanPropertyWriter bpw = null;
try {
Class cc = beanDesc.getType().getRawClass();
Method m = cc.getMethod("getClass", null);
bpw = new BeanPropertyWriter("$className", null, null, m, null,true, null);
} catch (SecurityException e) {
// TODO
} catch (NoSuchMethodException e) {
// TODO
}
props.add(bpw);
return props;
This way i get more control and can do other kind of filters too.
#JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME)
This annotation works perfectly, as suggested by Arun Prakash. I was trying to get json in this form:
{"Rowset":{"ROW":{"receiptno":"881604199388936","status":"SUCCESS"}}}
but getting like this:
{"ROW":{"receiptno":"881604199388936","status":"SUCCESS"}}
Now that annotation resolved my problem.
I would be interested in hearing the OP's solution for this. I'm having similar issues where my RESTful web service is serializing objects as either XML or JSON for clients. The Javascript clients need to know the wrapping type so that can parse it. Coupling the type to a URI pattern is not an option.
Thanks.
Edit: I noticed that Spring MappingJacksonJsonMarshaller adds the wrapping class when marshalling, so I stepped through the code in debug and noticed that Spring passes in a HashMap with a single key-value pair such that the key is the wrapping name and the value is the object. So, I extended JacksonJaxbJsonProvider, override the writeTo() method and added the following:
HashMap<String, Object> map = new HashMap<String, Object>();
map.put(value.getClass().getSimpleName(), value);
super.writeTo(map, type, genericType, annotations, mediaType, httpHeaders,entityStream);
It's a bit of a hack, but it works nicely.
use withRootName.
objectMapper.writer().withRootName(MyPojo.class.getName());
I have found through experience that it is a good idea for all JSON to include both the backend type (as a string) and the component type used to render it in the front end (if using something like angular or Vue).
The justification for doing this is so that you can process various types with a single set of code.
In vue, for example, having the name of the UI component in the data allows you, among other things, to have a screen rendering a list of children of different types using only a single tag in the parent template.
<component :is="child.componentType"/>.
For backend systems and web services - I prefer to use a single web service processor class that provides logging, auditing and exception handling for all web services by looking up the appropriate processor class based on the incoming payload. That makes the implementation of all my web services look exactly the same (about 3 lines of code), and I get detailed event logging through the lifecycle of the call without writing any per service code to do so.
Having the type wrapping the JSON makes it self documenting. If all you see are the properties, you have no idea what you are looking at until you find the corresponding end point.
If you want to write data driven software, being able to identify what you are processing is a basic requirement.