gpt4 book ai didi

java - 使用 ReferenceTypeDeserializer 与 Jackson & Spring 反序列化通用类型

转载 作者:行者123 更新时间:2023-11-30 12:07:06 26 4
gpt4 key购买 nike

我想我在这里遗漏了一些明显的东西,但我似乎无法使用 Spring/Kotlin/Jackson 反序列化一个简单的通用容器。

有问题的数据类型非常简单:

@JsonDeserialize(using = PatchableDeserializer::class)
sealed class Patchable<T> {
class Undefined<T>: Patchable<T>()
class Null<T>: Patchable<T>()
data class Present<T>(val content: T): Patchable<T>()
// …
}

反序列化器扩展了 ReferenceTypeDeserializer,就像 jdk8 模块的 OptionalDeserializer 一样。

class PatchableDeserializer(javaType: JavaType, vi: ValueInstantiator, typeDeser: TypeDeserializer, deser: JsonDeserializer<*> ):
ReferenceTypeDeserializer<Patchable<*>>(javaType, vi, typeDeser, deser) {
// …
}

我假设 Jackson 会在这里填写 PatchableDeserializer 的构造函数参数。然而,情况似乎并非如此:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'my.namespace.PatchableDeserializer': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.JavaType' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

我假设 Jackson 提供了 javaType 的值,因为我无法在编译时知道它。

这是我用来测试的代码,它会生成上述异常:

@RunWith(SpringRunner::class)
@JsonTest
class PatchableTest {
@Autowired
lateinit var objectMapper: ObjectMapper

@Test
fun patchableDeserialisesStringValue() {
val value: Patchable<String> = objectMapper.readValue("\"null\"", object: TypeReference<Patchable<String>>() {})
assertTrue(value.isPresent())
assertEquals("null", value.unsafeGetValue())
}
}

我错过了什么?此外,我真的很难在网上查找有关如何反序列化泛型类型的一些信息,因此,如果有人指出如何为泛型类型编写自定义反序列化程序,我将不胜感激。

最佳答案

通用容器的反序列化(那些扩展 com.fasterxml.jackson.databind.deser.std.ReferenceTypeDeserializer)不能以这种方式注册。

您需要使用重写的 findReferenceDeserializer 方法注册 com.fasterxml.jackson.databind.deser.Deserializers 的自定义实现。调用此方法为引用类型的值定位反序列化器。

您还需要添加 com.fasterxml.jackson.databind.type.TypeModifier 将您的“通用容器”类型修改为 com.fasterxml.jackson.databind .type.ReferenceType,因此您的自定义 ReferenceTypeDeserializer 将被调用。

最简单的方法是注册一个自定义 com.fasterxml.jackson.databind.Module,它将addDeserializersaddTypeModifier。这种模块的一个很好的例子是 Jdk8Module它为 java.util.Optional 及其 friend 完成所有工作。

为自定义通用容器注册反序列化器的完整示例MyRef:

MyRefDeserializer 完成主要工作。这只是一个例子。覆盖方法的确切实现(以及需要覆盖哪些方法)将取决于您的要求。

public final class MyRefDeserializer extends ReferenceTypeDeserializer<MyRef<?>> {

public MyRefDeserializer(JavaType fullType, ValueInstantiator vi,
TypeDeserializer typeDeser, JsonDeserializer<?> deser) {
super(fullType, vi, typeDeser, deser);
}

@Override
protected MyRefDeserializer withResolved(TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser) {
return new MyRefDeserializer(_fullType, _valueInstantiator, typeDeser, valueDeser);
}

@Override
public MyRef<?> getNullValue(DeserializationContext ctxt) throws JsonMappingException {
return MyRef.of(_valueDeserializer.getNullValue(ctxt));
}

@Override
public Object getAbsentValue(DeserializationContext ctxt) throws JsonMappingException {
return MyRef.absent();
}

@Override
public MyRef<?> referenceValue(Object contents) {
return MyRef.of(contents);
}

@Override
public Object getReferenced(MyRef<?> reference) {
return reference.orElse(null);
}

@Override
public NullableOptional<?> updateReference(MyRef<?> reference, Object contents) {
return referenceValue(contents);
}

}

Deserializers接口(interface)的实现:

public class MyRefDeserializers extends Deserializers.Base {

@Override
public JsonDeserializer<?> findReferenceDeserializer(ReferenceType refType, DeserializationConfig config,
BeanDescription beanDesc, TypeDeserializer contentTypeDeserializer, JsonDeserializer<?> contentDeserializer)
throws JsonMappingException {

if (refType.hasRawClass(MyRef.class)) {
return new MyRefDeserializer(refType, null, contentTypeDeserializer, contentDeserializer);
}

return null;
}

}

TypeModifier 的实现允许 Jackson 理解 MyRef 是一个 ReferenceType 类型:

public class MyRefTypeModifier extends TypeModifier {

@Override
public JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, TypeFactory typeFactory) {
if (type.isReferenceType() || type.isContainerType()) {
return type;
}

if (type.getRawClass() == MyRef.class) {
return ReferenceType.upgradeFrom(type, type.containedTypeOrUnknown(0));
} else {
return type;
}

}

}

最后,Module 将注册反序列化器和类型修饰符:

public class MyRefModule extends Module {

@Override
public String getModuleName() {
return "MyRefModule";
}

@Override
public Version version() {
return Version.unknownVersion();
}

@Override
public void setupModule(SetupContext context) {
context.addDeserializers(new MyRefDeserializers());
context.addTypeModifier(new MyRefTypeModifier());
}

}

然后您需要使用 ObjectMapper 注册此模块。例如:

ObjectMapper = new ObjectMapper();
objectMapper.registerModule(new MyRefModule());

如果您使用 Spring Boot,您只需将模块公开为 @Bean,它将自动注册到默认的 ObjectMapper 中。

关于java - 使用 ReferenceTypeDeserializer 与 Jackson & Spring 反序列化通用类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55166379/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com