I am facing the following problems. When Jackson serializer is passed a wrapped primitive for serialization, this primitive is serialized as is, for example:
objectMapper = new ObjectMapper();
StringWriter w = new StringWriter();
objectMapper.writeValue(w, Integer.valueOf(10));
System.out.println(w.toString());
produces 10
as output. However 10
is not a valid JSON (according to jsonlint) and should be wrapped either in square brackets ([10]
, so it will be a single-element array) or in curly brackets ({value:10}
, so it will be an object with dummy property). The problem affects numbers, java.lang.String
, java.util.Date
, ...
My question is: How to make Jackson to perform the wrapping? Shouldn't Jackson always produce a valid JSON?
I have analyzed Jackson behavior with SerializationConfig.Feature.WRAP_ROOT_VALUE
enabled: it does not work as I expect. Primitives are now serialized to valid JSON ({"Integer":10}
), but "normal" Java beans are also wrapped which is undesired ({"MyBean":{"field":value, ...}}
instead of {"field":value, ...}
).
If somebody can advise how to customize Jackson, perhaps with custom serializer. The difficulty would be that it is necessary to distinguish the root primitive wrapper (which needs to be wrapped) from bean primitive property (does not need to be wrapped).
To make the story complete: Jackson serializer is used as message convertor for Spring MVC, and I doubt it is fairly easy to write a hook to intercept serialization of primitives (which will not call Jackson but will simply return "[" + String.toString(obj) + "]"
when necessary). So I would prefer the solution with tuned Jackson.
2. Standard Serialization of an Object Graph Let's define 2 simple entities and see how Jackson serializes these without any custom logic: Now, let's serialize an Item entity with a User entity: This will result in a full JSON representation for both entities: 3. Custom Serializer on the ObjectMapper
Serialization annotations are used when we serialize Java objects into a Json string. Jackson library provides several serialization annotations such as @JsonSerialize, @JacksonGetter, @JsonAnyGetter, etc. Let's understand each one of them one by one with an example.
Also, on the writing side, we can use the writeValue API to serialize any Java object as JSON output. We'll use the following Car class with two fields as the object to serialize or deserialize throughout this article: 3.1. Java Object to JSON
The main class in the Jackson library for reading and writing JSON is ObjectMapper. It's in the com.fasterxml.jackson.databind package and can serialize and deserialize two types of objects: If you already have a domain class, a POJO, you can convert between that class and JSON by providing the class to the ObjectMapper.
Finally the custom serializer
import java.io.IOException;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.ser.std.ScalarSerializerBase;
public class NumberSerializer extends ScalarSerializerBase<Number> {
protected NumberSerializer() {
super(Number.class);
}
@Override
public void serialize(Number value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
JsonGenerationException {
if (jgen.getOutputContext().inRoot()) {
jgen.writeStartArray();
jgen.writeNumber(value.longValue());
jgen.writeEndArray();
}
else {
jgen.writeNumber(value.longValue());
}
}
}
did a job for me. Serializer can be registered as module (see here).
Note when using this seriazlier: As it turns all primitive into primitive arrays with only one element, is breaks the reflection principle in a sense that A != desrialize(serialize(A))
where A
is some primitive.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With