如何在XmlMapper反序列化中使用注入 [英] How to use injection with XmlMapper deserialization
问题描述
我正在使用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
具有 findInjectableValue 方法,该方法允许在上下文中按名称查找先前注册的bean.在下面,您可以找到示例,该示例显示了如何执行此操作的一般思路.首先,声明一个我们要自动装配的可注射bean:
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屋!