如何将依赖注入 Jackson 自定义反序列化器

Sas*_*wat 8 java guice jackson json-deserialization

我想启用一些 String 类型字段的自定义 jackson 反序列化器。反序列化器还需要注入一个基于 guice 的依赖 bean。示例代码如下:

public class CustomDeserializer extends StdDeserializer<String> {

    private SomeDependecy dependency;

    public StringDeserializer() {
        this(null);
    }

    public StringDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return dependency.perform(p.getValueAsString());
    }
}
Run Code Online (Sandbox Code Playgroud)

我无法注册基于 Class 类型的模块,因为它是通用的(String.class、Complex Datatype(但并非每个模块都需要自定义反序列化器))。有没有办法在不使用静态方法的情况下实现上述目标?

PS:我确实搜索了网络,但如果不使用 statics 就找不到更干净的解决方案。使用一些静态方法来获取上下文和 bean 的所有建议。

Sas*_*wat 5

看起来有另一种方法(感谢我的一位同事)在 objectMapper 实例上使用 injectableValues ,然后通过 DeserializationContext ctxt 获取依赖项。以下是代码。

ObjectMapper guice 模块。

public class MerchantConverterModule extends AbstractModule {

    @Override
    protected void configure() {

    }

    @Provides
    @Singleton
    public ObjectMapper objectMapper() {

        ObjectMapper objectMapper = new ObjectMapper();

        /**
         * Add dependency object to object mapper.
         */
        objectMapper.setInjectableValues(new InjectableValues
            .Std()
            .addValue("DependencyName", dependency));

        return objectMapper;
    }


}
Run Code Online (Sandbox Code Playgroud)

自定义解串器的代码

public class CustomDeserializer extends StdDeserializer<String> {

    private SomeDependecy dependency;

    public StringDeserializer() {
        this(null);
    }

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return getDependency(ctxt).perform(p.getValueAsString());
    }

    private SomeDependency getDependency(DeserializationContext ctxt) {
        SomeDependency dependency = (SomeDependency) ctxt
                .findInjectableValue("DependencyName", null, null);

        return dependency;
    }
}
Run Code Online (Sandbox Code Playgroud)

findInjectableValue 方法是最终方法,因此您可能需要调整单元测试代码以模拟决赛。

注意:缺点是对象映射器和解串器之间存在紧密耦合。


Mic*_*ber 3

看一下ContextualDeserializer界面。来自文档:

JsonDeserializers 可以实现附加接口来获取回调,该回调可用于创建反序列化器的上下文(上下文相关)实例,以用于处理支持类型的属性。这对于可以通过注释配置的反序列化器很有用,或者应该根据反序列化的属性类型而具有不同的行为。

假设您有简单的解密接口和实现结构。

interface Dependency {

    String decrypt(String value);
}

class SomeDependency implements Dependency {

    public SomeDependency() {
        System.out.println("Create new SomeDependency!");
    }

    @Override
    public String decrypt(String value) {
        return value.replace('a', 'A');
    }
}

class DecryptModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(Dependency.class).to(SomeDependency.class);
    }
}
Run Code Online (Sandbox Code Playgroud)

您的自定义反序列化器可能如下所示:

class DecryptDeserializer extends StdDeserializer<String> implements ContextualDeserializer {

    private Dependency dependency;

    public DecryptDeserializer() {
        super(String.class);
    }

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        return dependency.decrypt(p.getValueAsString());
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        Injector injector = Guice.createInjector(new DecryptModule());
        DecryptDeserializer deserializer = new DecryptDeserializer();
        deserializer.dependency = injector.getInstance(Dependency.class);

        return deserializer;
    }
}
Run Code Online (Sandbox Code Playgroud)

createContextual方法用于创建新的反序列化器实例。如何创建它有多种选择。您甚至可以将此解决方案与静态注入混合使用。