如何在XmlMapper反序列化中使用注入 [英] How to use injection with XmlMapper deserialization

查看:105
本文介绍了如何在XmlMapper反序列化中使用注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用com.fasterxml.jackson.dataformat.xml中的XmlMapper. 我正在序列化的类具有未序列化的Autowired成员.

I am using XmlMapper from com.fasterxml.jackson.dataformat.xml. The class, I am serializing, has an Autowired member that is not serialized.

我希望能够将XML反序列化为实例,并使用Spring填充自动装配的成员变量.

I want to be able to deserialize the XML into an instance and have the autowired member variable populated by Spring.

有没有办法做到这一点?

Is there a way to do this?

推荐答案

ObjectMapper具有

ObjectMapper has setInjectableValues method which allow to register some external beans which we want to use during serialisation/deserialisation. For example, DeserializationContext class has findInjectableValue method which allow to find previously registered bean in context by name. Below you can find example which shows general idea how to do that. First, declare an injectable bean which we want to autowire:

class InjectBean {

    private int key = ThreadLocalRandom.current().nextInt();

    @Override
    public String toString() {
        return "key => " + key;
    }
}

我们要从XML反序列化的

POJO类如下所示:

POJO class which we want to deserialise from XML could look like below:

class Pojo {

    private String name;
    private InjectBean dependency;

    // getters, setters, toString
}

现在,我们需要实现自定义反序列化程序,该反序列化程序将注入自动装配的字段:

Now, we need to implement custom deserialiser which will inject autowired field:

class PojoBeanDeserializer extends BeanDeserializer {

    public static final String DEPENDENCY_NAME = "injectBean";

    public PojoBeanDeserializer(BeanDeserializerBase src) {
        super(src);
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        Object deserialize = super.deserialize(p, ctxt);
        InjectBean injectableValue = findInjectableValue(ctxt);

        Pojo pojo = (Pojo) deserialize;
        pojo.setDependency(injectableValue);

        return deserialize;
    }

    private InjectBean findInjectableValue(DeserializationContext context) throws JsonMappingException {
        return (InjectBean) context.findInjectableValue(DEPENDENCY_NAME, null, null);
    }
}

以上反序列化器只能用于Pojo类.如果您需要对许多类执行相同的操作,则可以将setDependency方法提取到接口,并通过需要以相同方式处理的每个POJO在该接口上实现此方法.在上面的反序列化器中,您可以强制转换为接口,而不必强制转换为Pojo.要注册我们的自定义解串器,我将使用BeanDeserializerModifier,但您可以通过其他方式进行.例如,如果您已经具有自定义反序列化程序,并且使用@JsonDeserialize批注,则无需这样做.简单用法如下所示:

Above deserialiser could be used only for Pojo class. If you need to do the same for many classes you can extract setDependency method to an interface and implement it this interface by each POJO you need to handle in the same way. In above deserialiser instead of casting to Pojo you can cast to your interface. To register our custom deserialiser I will use BeanDeserializerModifier but you can do that in other way. For example if you already have custom deserialiser and you use @JsonDeserialize annotation you do not need to do that. Simple usage could look like below:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.ThreadLocalRandom;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        File xmlFile = new File("./resource/test.xml").getAbsoluteFile();

        InjectBean injectBean = autowire();
        InjectableValues.Std injectableValues = new InjectableValues.Std();
        injectableValues.addValue(PojoBeanDeserializer.DEPENDENCY_NAME, injectBean);

        SimpleModule injectModule = new SimpleModule();
        injectModule.setDeserializerModifier(new InjectBeanDeserializerModifier());

        XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.registerModule(injectModule);
        xmlMapper.setInjectableValues(injectableValues);

        Pojo bean = xmlMapper.readValue(xmlFile, Pojo.class);

        System.out.println("After deserialization:");
        System.out.println(bean);
    }

    private static InjectBean autowire() {
        InjectBean bean = new InjectBean();

        System.out.println("Injectable bean from context: " + bean);

        return bean;
    }
}

class InjectBeanDeserializerModifier extends BeanDeserializerModifier {
    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        if (beanDesc.getType().getRawClass() == Pojo.class) {
            JsonDeserializer<?> jsonDeserializer = super.modifyDeserializer(config, beanDesc, deserializer);

            return new PojoBeanDeserializer((BeanDeserializer) jsonDeserializer);
        }

        return super.modifyDeserializer(config, beanDesc, deserializer);
    }
}

对于XML有效载荷以下:

<Pojo>
    <name>Tom</name>
</Pojo>

打印:

Injectable bean from context: key => 909636975
After deserialization:
Bean{name='Tom', dependency=key => 909636975}

另请参阅:

这篇关于如何在XmlMapper反序列化中使用注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆