简体   繁体   English

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

[英]How to inject dependency into Jackson Custom deserializer

I want to enable a custom jackson deserializer of some fields of type String.我想启用一些 String 类型字段的自定义 jackson 反序列化器。 The deserializer also needs to be injected with a guice based dependency bean.反序列化器还需要注入一个基于 guice 的依赖 bean。 SampleCode below:示例代码如下:

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

I cannot register a module based on Class type as it is generic (String.class, Complex Datatype( but not every one require a custome deserializer)).我无法注册基于 Class 类型的模块,因为它是通用的(String.class、Complex Datatype(但并非每个模块都需要自定义反序列化器))。 Is there a way to achieve the above without using static methods?有没有办法在不使用静态方法的情况下实现上述目标?

PS: I did search net but could not find a cleaner solution without using statics . PS:我确实搜索了网络,但如果不使用 statics 就找不到更干净的解决方案。 All the suggestions where around using Some static method to get context and bean.使用一些静态方法来获取上下文和 bean 的所有建议。

Looks like there is another approach (Thanks to one of my colleague) using injectableValues on the objectMapper instance and then fetch the dependency through DeserializationContext ctxt.看起来还有另一种方法(感谢我的一位同事)在 objectMapper 实例上使用 injectableValues,然后通过 DeserializationContext ctxt 获取依赖项。 Following is the code.以下是代码。

ObjectMapper guice module. 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;
    }


}

Code of your custom deserializer自定义解串器的代码

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

findInjectableValue method is a final method, so you might need to tweak your unit test code to mock finals. findInjectableValue方法是最终方法,因此您可能需要调整单元测试代码以模拟决赛。

NOTE: The drawback is that there is a tight coupling between the objectmapper and deserializer.注意:缺点是对象映射器和解串器之间存在紧密耦合。

Take a look on ContextualDeserializer interface.看看ContextualDeserializer接口。 From documentation:从文档:

Add-on interface that JsonDeserializers can implement to get a callback that can be used to create contextual (context-dependent) instances of deserializer to use for handling properties of supported type. JsonDeserializers 可以实现的附加接口以获取回调,该回调可用于创建反序列化器的上下文(依赖于上下文)实例以用于处理支持类型的属性。 This can be useful for deserializers that can be configured by annotations, or should otherwise have differing behavior depending on what kind of property is being deserialized.这对于可以通过注释配置的反序列化器很有用,或者应该具有不同的行为,具体取决于正在反序列化的属性类型。

Let's assume you have simple decrypt interface and implementation structure.假设您有简单的解密接口和实现结构。

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

You custom deserialiser could look like this:您的自定义反序列化程序可能如下所示:

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

createContextual method is used to create new deserialiser instance. createContextual方法用于创建新的反序列化器实例。 You have many options how to create it.您有很多选择如何创建它。 You can even mix this solutions with Static Injection .您甚至可以将此解决方案与静态注入混合使用。

I haven't used Guice that much, but I guess it is somewhat similar to Spring.我没怎么用过 Guice,但我想它和 Spring 有点相似。 So I will post this answer here in case you can use the same principles as I have, and that someone using Spring reads this.因此,我将在此处发布此答案,以防您可以使用与我相同的原则,并且使用 Spring 的人会阅读此内容。

First I annotate my Jackson module as a Spring @Component:首先,我将我的 Jackson 模块注释为 Spring @Component:

@Component
public class FlowModule extends SimpleModule {

@Autowired private SupplierItemDeserializer supplierItemDeserializer;

This means I can get whatever I want @Autowired into my module.这意味着我可以将任何我想要的 @Autowired 放入我的模块中。 As you can see I have also done the same thing to my de- / serializers:正如你所看到的,我也对我的反/序列化器做了同样的事情:

@Component
public class SupplierItemDeserializer extends JsonDeserializer<SupplierItem> {

@Autowired
private SupplierService supplierService;

I have then created a service that deals with converting to and from JSON:然后我创建了一个服务来处理与 JSON 之间的转换:

@Service
public class IOService {

private ObjectMapper mapper;

@Autowired
private FlowModule flowModule;


@PostConstruct
public void initialize() {
    mapper = new ObjectMapper();
    mapper.registerModule(flowModule);
    mapper.registerModule(new JavaTimeModule());
}

The service contains an ObjectMapper with my module that has all the right wiring.该服务包含一个带有我的模块的 ObjectMapper,它具有所有正确的接线。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM