我想启用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。
看起来还有另一种方法(感谢我的一位同事)在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,该模块具有所有正确的接线。