【问题标题】:Jackson ObjectMapper using custom Serializers and DeserializersJackson ObjectMapper 使用自定义序列化器和反序列化器
【发布时间】:2014-02-11 15:45:32
【问题描述】:

我有一个配置 Jackson ObjectMapper 的类。它为我的对象类型添加了一些自定义序列化器和反序列化器,如下所示:

public class JsonMapperFactory {
    public static ObjectMapper createObjectMapper() {
        final SimpleModule module = new SimpleModule("customerSerializationModule", new Version(1, 0, 0, "static version"));
        addCustomDeserializersTo(module);
        addCustomSerializersTo(module);

        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(module);
        return objectMapper;
    }
    private static void addCustomSerializersTo(final SimpleModule module) {
        module.addSerializer(DateTime.class, new DateTimeSerializer());
    }
    private static void addCustomDeserializersTo(final SimpleModule objectMapper) {
        objectMapper.addDeserializer(DateTime.class, new DateTimeDeserializer());
    }
}

我已经在他们自己的测试类中测试了我的客户序列化器,所以在我对这个 JsonMapperFactory 类的测试中,我试图简单地检查创建的 ObjectMapper 是否具有预期的序列化器(或反序列化器)这可以实现通过自省 ObjectMapper,但它似乎没有任何机制可以做到这一点。

有谁知道测试的好方法吗?

对于反序列化器,我有以下内容:

private void assertThatObjectMapperUsesCorrectDeserializer(final Class<?> typeClazz, final Class<?> deserializerClazz) throws JsonMappingException {
    final  DeserializationConfig deserializationConfig = this.objectMapper.getDeserializationConfig();
    final JsonDeserializer<Object> deserializer = this.objectMapper.getDeserializerProvider().findTypedValueDeserializer(deserializationConfig, javaTypeFor(typeClazz), null);
    assertThat(deserializer, is(instanceOf(deserializerClazz)));
}
private JavaType javaTypeFor(final Class<?> clazz) {
    return TypeFactory.type(clazz); //deprecated method :(
}

这非常冗长并且使用了不推荐使用的方法。

我还没有找到对序列化程序进行类似测试的方法。所以我目前采用序列化一个对象并检查它是否正确序列化(本质上是重复序列化程序测试)

非常欢迎任何想法。

【问题讨论】:

  • 如果你的类使用了构建器,那测试起来会容易得多;通过这种方式,您可以传递 {de,} 序列化程序的模拟,甚至测试它们是否按照您的期望进行序列化。
  • com.fasterxml.jackson.databind.type.SimpleType.construct() 方法似乎执行与您示例中已弃用的 TypeFactory.type() 相同的功能。
  • 事件更好,ObjectMapper.getTypeFactory() 返回一个带有很多工厂“constructSomeType”方法的 TypeFactory 实例。但是这些类和方法来自jackson 2.3,所以我假设你还在使用1.9分支。
  • @OlegEstekhin 实际上我发现前几天。我不得不遍历并更新项目中的所有依赖项,并找到了新的类型工厂实例方法。

标签: java testing serialization jackson deserialization


【解决方案1】:

根据此处提供的答案和 cmets,我最近重新设计了该课程,以便为 ModuleObjectMapper 使用构建器。这使我能够提供模拟并检查是否将正确的(反)序列化程序添加到模块中,然后按预期将模块注册到对象映射器。

对象映射器生成器:

public class ObjectMapperBuilder {
    ObjectMapper mapper;

    public ObjectMapperBuilder configure(final ObjectMapper mapper) {
        this.mapper = mapper;
        return this;
    }

    public ObjectMapperBuilder withModule(final Module module) {
        this.mapper.registerModule(module);
        return this;
    }

    public ObjectMapper build() {
        return this.mapper;
    }
}

模块构建器:

public class SimpleModuleBuilder {
    SimpleModule module;

    public SimpleModuleBuilder configure(final SimpleModule module) {
        this.module = module;
        return this;
    }

    public <X> SimpleModuleBuilder withSerializer(final Class<X> clazz, final JsonSerializer<X> serializer) {
        this.module.addSerializer(clazz, serializer);
        return this;
    }

    public <X> SimpleModuleBuilder withDeserializer(final Class<X> clazz, final JsonDeserializer<X> deserializer) {
        this.module.addDeserializer(clazz, deserializer);
        return this;
    }

    public SimpleModule build() {
        return this.module;
    }
}

最后,新的 JsonMapperFactory:

public class JsonMapperFactory {

    public static ObjectMapper configureObjectMapper(final ObjectMapper mapper, final SimpleModule module) {
        final SimpleModuleBuilder modulebuilder = new SimpleModuleBuilder();

        final SimpleModule configuredModule = modulebuilder.configure(module)
            .withSerializer(DateTime.class, new DateTimeSerializer())
            .withDeserializer(DateTime.class, new DateTimeDeserializer())
            .build();

        final ObjectMapperBuilder objectMapperBuilder = new ObjectMapperBuilder();
        return objectMapperBuilder.configure(mapper).withModule(configuredModule).build();
    }
}

工厂方法仍在 Spring 配置中使用,但配置现在实例化空白 ModuleObjectMapper,然后将它们提供给工厂方法,然后再配置它们。

【讨论】:

    【解决方案2】:

    如果JsonDeserializer(和DateTimeDeserializer也是)是一个接口,您可以轻松地“JMock”它,将模拟实例传递给JsonMapperFactory#createObjectMapper,然后期望您的自定义“序列化”方法恰好调用1次;例如

    DateTimeSerializer serializer = context.mock(DateTimeSerializer.class);
    DateTimeDeserializer serializer = context.mock(DateTimeDeserializer.class);
    ObjectMapper mapper = JacksonMapperFactory.createObjectMapper(deserializer, serializer);
    
    exactly(1).of(jsonDeserializer).serialize(myDateTime,
      with(any(JsonGenerator.class),
      with(any(SerializerProvider.class)))
    

    作为一个具体的类,您可以改为定义一个新的(测试范围的)De/Serializer 来扩展您的自定义 DateTime(De)serializer,并简单地计算对其的调用:

    private static class DateTimeDeserializerWithCounter extends DateTimeDeserializer {
        public int counter = 0;
    
        @Override
        public DateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            counter++;
            return super.deserialize(jsonParser, deserializationContext);
        }
    }
    
    @Test
    public void usageTest(){
        //init mapper with the above DateTimeDeserializerWithCounter - see below
        mapper.readValue("...", DateTime.class);
        Assert.assertEquals(1, deserializer.counter);
    }
    

    下面是一个更“面向测试”的工厂的快照:

    //package visibility, to allow passing different De/Serializers while testing
    static ObjectMapper createObjectMapper(JsonDeserializer deserializer, JsonSerializer serializer) {
        final SimpleModule module = new SimpleModule("customerSerializationModule", new Version(1, 0, 0, "static version"));
        module.addDeserializer(DateTime.class, deserializer);
        module.addSerializer(DateTime.class, serializer);
    
        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(module);
        return objectMapper;
    }
    
    //production method: no-args, as in the original version
    public static ObjectMapper createObjectMapper() {
        return createObjectMapper(new DateTimeDeserializer(), new DateTimeSerializer());
    }
    

    希望对您有所帮助。

    【讨论】:

    • 我确实喜欢这个想法,但我认为这是在测试错误的东西。我将 Jackson 用作一个经过验证的库,所以我愿意接受它按我的预期工作。您的解决方案依赖于 De/Serializing 对象并检查 Jackson 库是否调用了我的 de/serializers。所以它几乎是在测试杰克逊图书馆。我想知道是否有一种方法可以内省 ObjectMapper,或者重新设计创建以允许我测试它是按我的预期创建的。我一直在考虑构建器/工厂类,然后在测试中进行模拟。
    • 您的解决方案也让我想到了重新设计 JsonMapperFactory 类的想法,这是我以前没有考虑过的。
    猜你喜欢
    • 2019-08-10
    • 2016-02-13
    • 2011-04-12
    • 2018-07-15
    • 2017-09-10
    • 2014-08-02
    • 1970-01-01
    • 2020-05-27
    • 2016-01-29
    相关资源
    最近更新 更多