Jackson序列化object时的一个坑

概要

这几天,我们的系统里连续出现了2个故障,故障原因很简单。我们在domain里平增了一个get方法,但是这个get方法并不是一般JAVA Bean里的getter/setter.
对这个domain进行JSON序列化时,jackson抛出了null pointer异常。

如果在Jackson序列化时将REQUIRE_SETTERS_FOR_GETTERS 设置为true,可以解决这个问题。

原因 && 解决方法

产生这个问题的根本原因是,以上的两个常见的JSON序列化工具在序列化domain时,会调用domain里的所有get方法。

解决这个事情的解决方法有很多

  1. 最好不要在domain里写复杂的处理逻辑
  2. 写public方法时,需要做非空校验
  3. 写的public方法换一个名字,不以get 开头

对于jackson来说,最好的解决方法是,给ObjectMapper增加一个配置

objectMapper.configure(SerializationConfig.Feature.REQUIRE_SETTERS_FOR_GETTERS, true);

REQUIRE_SETTERS_FOR_GETTERS 的说明

如果这个配置为false时,只有存在对应的构造器、setter或者field时,才调用getter。代码里的注释是

        /**
         * Feature that determines whether getters (getter methods)
         * can be auto-detected if there is no matching mutator (setter,
         * constructor parameter or field) or not: if set to true,
         * only getters that match a mutator are auto-discovered; if
         * false, all auto-detectable getters can be discovered.
         *<p>
         * Feature is disabled by default for backwards compatibility
         * reasons.
         * 
         * @since 1.9
         */
        REQUIRE_SETTERS_FOR_GETTERS(false),

测试代码

以上配置的测试代码是

public class JacksonTest extends TestCase{

    private static ObjectMapper objectMapper;

    Foo foo = new Foo();

    @Test
    public void testOriginMapper() throws  Exception{
        objectMapper = new ObjectMapper();

        try {
            objectMapper.writeValueAsString(foo);
            failBecauseExceptionWasNotThrown(NullPointerException.class);
        } catch (Exception e) {
            assertThat(e).hasMessageContaining("NullPointerException");
        }
    }

    @Test
    public void testNoGetter() throws  Exception{
        objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationConfig.Feature.REQUIRE_SETTERS_FOR_GETTERS, true);

        assertThat(objectMapper.writeValueAsString(foo)).isEqualTo("{\"name\":null,\"age\":null}");
    }
}

class Foo {
    String name;

    Integer age;

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getTrimName() {
        return name.trim();
    }
}

猜你喜欢

转载自gambol1234.iteye.com/blog/2147908