1. 概述

本文我们将进一步学习 Jackson 的高级用法 - 如何处理Map中的Null Value或Null Key

2. 忽略Map中的所有Null值

在 Jackson 中我们通过设置 ObjectMapper 全局控制null值序列化行为:

ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);

下面示例中,值为null的元素不会被序列化,即结果中不会包含 dtoObject2 字段:

@Test
public void givenIgnoringNullValuesInMap_whenWritingMapObjectWithNullValue_thenIgnored() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializationInclusion(Include.NON_NULL);

    MyDto dtoObject1 = new MyDto();

    Map<String, MyDto> dtoMap = new HashMap<String, MyDto>();
    dtoMap.put("dtoObject1", dtoObject1);
    dtoMap.put("dtoObject2", null);

    String dtoMapAsString = mapper.writeValueAsString(dtoMap);

    assertThat(dtoMapAsString, containsString("dtoObject1"));
    assertThat(dtoMapAsString, not(containsString("dtoObject2")));
}

3. Map 序列化键名为Null的字段

默认情况下,Jackson 不允许序列化key为null的Map,会抛出下面的异常:

c.f.j.c.JsonGenerationException: 
  Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)
    at c.f.j.d.s.i.FailingSerializer.serialize(FailingSerializer.java:36)

当然,Jackson非常灵活,为了解决这一问题,我们可以自定义一个 null key serializer 重写默认的序列化行为:

class MyDtoNullKeySerializer extends StdSerializer<Object> {
    public MyDtoNullKeySerializer() {
        this(null);
    }

    public MyDtoNullKeySerializer(Class<Object> t) {
        super(t);
    }

    @Override
    public void serialize(Object nullKey, JsonGenerator jsonGenerator, SerializerProvider unused) 
      throws IOException, JsonProcessingException {
        jsonGenerator.writeFieldName("");
    }
}

该实现中我们用空字符串替换null key。

测试用例:

@Test
public void givenAllowingMapObjectWithNullKey_whenWriting_thenCorrect() 
throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.getSerializerProvider().setNullKeySerializer(new MyDtoNullKeySerializer());

    MyDto dtoObject = new MyDto();
    dtoObject.setStringValue("dtoObjectString");

    Map<String, MyDto> dtoMap = new HashMap<String, MyDto>();
    dtoMap.put(null, dtoObject);

    String dtoMapAsString = mapper.writeValueAsString(dtoMap);

    assertThat(dtoMapAsString, containsString("\"\""));
    assertThat(dtoMapAsString, containsString("dtoObjectString"));
}

4. 忽略 Null 字段

除了Map, Jackson 提供了很多配置和方法处理 null 字段,感兴趣的朋友可以参考这篇教程

5. 总结

Map序列化很常见,Jackson 为此提供了很多配置方法,方便我们能更好的控制输出结果。

它同样提供了许多方法来处理Collection类型的数据

最后,本文示例中的所有源码,可以在[GitHub](https://github.com/eugenp/tutorials/tree/master/jackson-modules/jackson-conversions#readme "Github Project covering all Jackson examples)上找到。