如何将依赖项注入Jackson Custom反序列化器

时间:2019-02-20 01:44:25

标签: java jackson guice json-deserialization

我想启用一些String类型字段的自定义杰克逊解串器。解串器还需要注入基于guice的依赖项bean。下面的SampleCode:

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

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

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

3 个答案:

答案 0 :(得分:1)

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

您的自定义解串器可能如下所示:

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方法用于创建新的反序列化器实例。您有许多如何创建它的选择。您甚至可以将此解决方案与Static Injection混合使用。

答案 1 :(得分:1)

好像还有另一种方法(感谢我的一位同事),在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;
    }


}

自定义解串器的代码

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方法是final方法,因此您可能需要调整单元测试代码以模拟final。

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

答案 2 :(得分:0)

我没那么多使用Guice,但我想它有点类似于Spring。因此,我将在此处发布此答案,以防您可以使用与我相同的原理,并且使用Spring的人可以阅读此答案。

首先,我将Jackson模块注释为Spring @Component:

@Component
public class FlowModule extends SimpleModule {

@Autowired private SupplierItemDeserializer supplierItemDeserializer;

这意味着我可以将想要的任何东西@Autowired插入模块中。如您所见,我对我的解序列器也做了同样的事情:

@Component
public class SupplierItemDeserializer extends JsonDeserializer<SupplierItem> {

@Autowired
private SupplierService supplierService;

然后我创建了一个服务,该服务处理与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());
}

该服务包含一个ObjectMapper及其模块,该模块具有正确的接线。