提问者:小点点

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


我想启用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());
    }
}

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

PS:我做了搜索网络,但找不到一个更干净的解决方案,而不使用静态。所有的建议都围绕着使用一些静态方法来获取上下文和bean。


共3个答案

匿名用户

看起来还有另一种方法(感谢我的一位同事)在object tMapper实例上使用injectableValue,然后通过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;
    }
}

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

注意:缺点是对象映射器和反序列化器之间存在紧密耦合。

匿名用户

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

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

匿名用户

我没怎么用过Guice,但我想它有点类似于Spring。所以我将在这里发布这个答案,以防您可以使用与我相同的原则,并且使用Spring的人会阅读此内容。

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

@Component
public class FlowModule extends SimpleModule {

@Autowired private SupplierItemDeserializer supplierItemDeserializer;

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

@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,该模块具有所有正确的接线。