簡體   English   中英

如何將依賴注入 Jackson 自定義反序列化器

[英]How to inject dependency into Jackson Custom deserializer

我想啟用一些 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:我確實搜索了網絡,但如果不使用 statics 就找不到更干凈的解決方案。 使用一些靜態方法來獲取上下文和 bean 的所有建議。

看起來還有另一種方法(感謝我的一位同事)在 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方法是最終方法,因此您可能需要調整單元測試代碼以模擬決賽。

注意:缺點是對象映射器和解串器之間存在緊密耦合。

看看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方法用於創建新的反序列化器實例。 您有很多選擇如何創建它。 您甚至可以將此解決方案與靜態注入混合使用。

我沒怎么用過 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,它具有所有正確的接線。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM