Jackson How-To: Custom Serializers

2019/1/2 posted in  Java

https://github.com/FasterXML/jackson-docs/wiki/JacksonHowToCustomSerializers

Alternative Methods

There are two general mechanisms for enabling fully customized serialization:

  • Make value class (to be serialized) implement interface org.codehaus.jackson.map.JsonSerializableWithType: this is similar to implementing java.lang.Serializable in that a method (serialize()) of value class is called to handle serialization. ** NOTE: prior to Jackson 1.5, recommended interface was org.codehaus.jackson.map.JsonSerializable; but this is now deprecated since it did not support handling of possible additional type information (see JacksonPolymorphicDeserialization for details). ** If choosing to use this method, consider starting with a partial implementation such as org.codehaus.jackson.map.ser.SerializerBase (for any type} or org.codehaus.jackson.map.ser.ScalarSerializerBase (for serializers that output JSON Strings, Booleans or Number, aka scalar types)
  • Implement org.codehaus.jackson.map.JsonSerializer to create an external serializer that can be registered to handle values of certain types (or more specifically values of certain properties)

First method is simpler in that no registration is needed: when values of types that implement JsonSerializable are encountered, serialize() method is called to serialize the instance.

Registering external serializers

There are multiple ways to do register external serializers:

  • By using annotations:
    • Classes and methods can be annotated using @JsonSerialize.using (that takes serializer class as argument) to indicate type of serializer to use

With Jackson 1.7 and above

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:

    ObjectMapper mapper = new ObjectMapper();
    SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null));
    testModule.addSerializer(new MyCustomSerializer()); // assuming serializer declares correct class to bind to
    mapper.registerModule(testModule);

For more advanced handling of types to serializers you may need to implement Module interface directly; this will give more control over exact matching logic.
This may be necessary when dealing with generic types (especially Maps and Collections).

With Jackson 1.0 - 1.6

Before Jackson 1.7, the main method method was:

  • By using custom serializer factory (org.codehaus.jackson.map.SerializerFactory) *# Either use or extend existing implementation, org.codehaus.jackson.map.ser.CustomSerializerFactory (or even implement one from scratch if it doesn't work for you) *# Add mappings (from serialized Class to JsonSerializer instance) by calling addSpecificMapping or addGenericMapping (check out JavaDocs for explanation on difference) *# Custom serializer factory needs to be registered with ObjectMapper.setSerializerFactory to be used by ObjectMapper

Note that starting with 1.8, CustomSerializerFactory is deprecated and should not be used.

Specific use cases

Converting null values to something else

(like empty Strings)

If you want to output some other JSON value instead of null (mainly because some other processing tools prefer other constant values -- often empty String), things are bit trickier as nominal type may be anything; and while you could register serializer for Object.class, it would not be used unless there wasn't more specific serializer to use.

But there is specific concept of "null serializer" that you can use as follows:

// Configuration of ObjectMapper:
{
   // First: need a custom serializer provider
   StdSerializerProvider sp = new StdSerializerProvider();
   sp.setNullValueSerializer(new NullSerializer());
   // And then configure mapper to use it
   ObjectMapper m = new ObjectMapper();
   m.setSerializerProvider(sp);
   // Serialization as done using regular ObjectMapper.writeValue()
}

// and NullSerializer can be something as simple as:
public class NullSerializer extends JsonSerializer<Object>
{
   public void serialize(Object value, JsonGenerator jgen,
SerializerProvider provider)
       throws IOException, JsonProcessingException
   {
       // any JSON value you want...
       jgen.writeString("");
   }
}