Inquisitive Inquisitive - 3 months ago 51
JSON Question

How to prevent null values inside a Map and null fields inside a bean from getting serialized through Jackson

I have a a

Map<String,Foo> foosMap
that I want to serialize through Jackson . Now I want following two settings on the serialization process:


  1. The Map can have have plenty of null values and null keys and I don't want nulls to be serialized.

  2. For all those Foos that are getting serialized, I do not want to serialize null objects referenced inside Foo.



What is the best way to achieve this ? I am using jackson-core1.9 and jackson-mapper1.9 jars in my project.

Answer

If it's reasonable to alter the original Map data structure to be serialized to better represent the actual value wanted to be serialized, that's probably a decent approach, which would possibly reduce the amount of Jackson configuration necessary. For example, just remove the null key entries, if possible, before calling Jackson. That said...


To suppress serializing Map entries with null values, you can still make use of WRITE_NULL_MAP_VALUES, but note that it's moved to SerializationFeature:

mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);

To suppress serializing properties with null values, you can configure the ObjectMapper directly, or make use of the @JsonInclude annotation:

mapper.setSerializationInclusion(Include.NON_NULL);

or:

@JsonInclude(Include.NON_NULL)
class Foo
{
  public String bar;

  Foo(String bar)
  {
    this.bar = bar;
  }
}

To handle null Map keys, some custom serialization is necessary, as best I understand.

A simple approach to serialize null keys as empty strings (including complete examples of the two previously mentioned configurations):

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;

public class JacksonFoo
{
  public static void main(String[] args) throws Exception
  {
    Map<String, Foo> foos = new HashMap<String, Foo>();
    foos.put("foo1", new Foo("foo1"));
    foos.put("foo2", new Foo(null));
    foos.put("foo3", null);
    foos.put(null, new Foo("foo4"));

    // System.out.println(new ObjectMapper().writeValueAsString(foos));
    // Exception: Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)

    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
    mapper.setSerializationInclusion(Include.NON_NULL);
    mapper.getSerializerProvider().setNullKeySerializer(new MyNullKeySerializer());
    System.out.println(mapper.writeValueAsString(foos));
    // output: 
    // {"":{"bar":"foo4"},"foo2":{},"foo1":{"bar":"foo1"}}
  }
}

class MyNullKeySerializer extends JsonSerializer<Object>
{
  @Override
  public void serialize(Object nullKey, JsonGenerator jsonGenerator, SerializerProvider unused) 
      throws IOException, JsonProcessingException
  {
    jsonGenerator.writeFieldName("");
  }
}

class Foo
{
  public String bar;

  Foo(String bar)
  {
    this.bar = bar;
  }
}

To suppress serializing Map entries with null keys, further custom serialization processing would be necessary.