Java Jackson ObjectMapper 使用自定义序列化器和反序列化器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21706415/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 10:11:03  来源:igfitidea点击:

Hymanson ObjectMapper using custom Serializers and Deserializers

javatestingserializationHymansondeserialization

提问by Dan Temple

I've got a class that configures a Hymanson ObjectMapper. It adds in some custom serializers and deserializers for my object types as follows:

我有一个配置 Hymanson 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());
    }
}

I've tested my customer serializers within their own test classes, so in my test of this JsonMapperFactoryclass, I'm trying to simply check that the ObjectMapper created has the expected serializers (or deserializers) This could be achieve by introspecting the ObjectMapper, but it doesn't seem to have any mechanisms to do this.

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

Does anyone know of a nice way to test that?

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

For deserializers, I have the following:

对于解串器,我有以下内容:

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 :(
}

Which is quite verbose and uses deprecated methods.

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

I'm yet to find a way to do a similar test for the serializers. So I've currently resorted to serializing an object and check it serializes correctly (essentially duplicating the serializer test)

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

Any ideas are very welcome.

任何想法都非常受欢迎。

采纳答案by Dan Temple

From the answers & comments provided here, I recently redesigned the class to use builders for both the Moduleand the ObjectMapper. This allowed me to provide mocks and check that the correct (de)serializers were added to the module and then the module is registered to the object mapper as expected.

从这里提供的答案和意见,我最近重新设计的类使用建设者两者的ModuleObjectMapper。这允许我提供模拟并检查正确的(反)序列化器是否已添加到模块中,然后模块按预期注册到对象映射器。

Object Mapper Builder:

对象映射器生成器:

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;
    }
}

Module Builder:

模块生成器:

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;
    }
}

And finally, the new JsonMapperFactory:

最后,新的 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();
    }
}

The factory method is still used within Spring configuration, but the configuration now instantiates the blank Moduleand ObjectMapperbefore providing them to the factory methods that then configure them.

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

回答by PaoloC

If JsonDeserializer(and DateTimeDeserializertoo) was an interface, you could easily "JMock" it, pass mocked instance to JsonMapperFactory#createObjectMapperand then expect exactly 1 invocation of your custom "serialize" method; e.g.

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

DateTimeSerializer serializer = context.mock(DateTimeSerializer.class);
DateTimeDeserializer serializer = context.mock(DateTimeDeserializer.class);
ObjectMapper mapper = HymansonMapperFactory.createObjectMapper(deserializer, serializer);

exactly(1).of(jsonDeserializer).serialize(myDateTime,
  with(any(JsonGenerator.class),
  with(any(SerializerProvider.class)))

Being a concrete class, you can instead define a new (test-scoped) De/Serializer that extends your custom DateTime(De)serializer, and simply count invocation on that:

作为一个具体的类,您可以定义一个新的(测试范围的)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);
}

Below a snapshot of a more "test-oriented" Factory:

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

//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());
}

Hope that helps.

希望有帮助。