我正在尝试为所有从某个抽象类扩展或实现某个接口(interface)的类实现通用反序列化器。在此示例中,我使用接口(interface) StringConvertible
。我需要确定具体类型,以便创建实例。
安old forum post by Programmer Bruce引导我使用 ContextDeserializer 并且当 StringConvertible 是另一个类中的属性时它正在工作。
但是当我想直接反序列化StringConvertible时,我找不到获取具体类型的方法,因为beanProperty
参数是null
。根据this question/answer,显然这是预期的在 Jackson JSON 用户组:
属性应该为 null 的唯一情况是序列化“根值”时,这意味着对象实例直接传递给 ObjectMapper(或 ObjectWriter)的 writeValue() 方法——在这种情况下根本没有引用属性。但除此之外,它应该始终通过。
有关这两种情况的示例,请参见下面的主要方法:
@JsonDeserialize(using = StringConvertibleDeserializer.class)
public final class SomeStringConvertible implements StringConvertible {
private final String value;
public SomeStringConvertible(final String value) {
this.value = value;
}
@Override
@JsonValue
public String stringValue() {
return value;
}
}
public final class SomeWrapper {
public SomeStringConvertible stringConvertible;
public SomeWrapper() {
}
}
public class StringConvertibleDeserializer extends StdDeserializer<StringConvertible> implements ContextualDeserializer {
private final Class<? extends StringConvertible> targetClass;
StringConvertibleDeserializer() {
super(StringConvertible.class);
this.targetClass = null;
}
StringConvertibleDeserializer(final Class<? extends StringConvertible> targetClass) {
super(StringConvertible.class);
this.targetClass = targetClass;
}
@Override
public JsonDeserializer<?> createContextual(final DeserializationContext deserializationContext, @Nullable final BeanProperty beanProperty)
throws JsonMappingException {
final StringConvertibleDeserializer contextualDeserializer;
// ==== Determine target type =====
final Class<? extends StringConvertible> targetClass;
JavaType type = beanProperty.getType(); // -> beanProperty is null when the StringConvertible type is a root value
targetClass = (Class<? extends StringConvertible>) type.getRawClass();
// ==== Create contextual deserializer =====
contextualDeserializer = new StringConvertibleDeserializer(targetClass);
// ==== Return =====
return contextualDeserializer;
}
@Override
public StringConvertible deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
final StringConvertible value;
// ==== Create instance using the target type =====
if (targetClass.equals(SomeStringConvertible.class))
value = new SomeStringConvertible(jsonParser.getText());
else {
throw new RuntimeException();
}
// ==== Return =====
return value;
}
}
public final class JacksonModule extends SimpleModule {
public JacksonModule() {
super();
addDeserializer(StringConvertible.class, new StringConvertibleDeserializer());
}
}
public final class Main {
public static void main(String[] args) {
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JacksonModule());
final String wrappedValueJSON = "{\"stringConvertible\":\"hello world\"}";
final String rootValueJSON = "\"hello world\"";
try {
mapper.readValue(wrappedValueJSON, SomeWrapper.class); // This works fine
mapper.readValue(rootValueJSON, SomeStringConvertible.class); // This causes a NPE in createContextual(...) because beanProperty is null
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
问题:如果是根值,我怎样才能得到类型具体类型?或者,如果有比这更好的解决方案,您会推荐什么?
最佳答案
在研究 StaxMan 提出的解决方案时,我偶然发现了 this Github issue jackson-databind
解决了完全相同的问题。作为回应,维护者在 2.5.0
版本中为 DeserializationContext
添加了一个方法:
This turned out relatively easy to implement, so now there is:
class DeserializationContext {
public JavaType getContextualType() { ... }
}
which will give expected type during call to createContextual(), including case of deserializers that are directly added via annotation.
因此,为了在我的案例中实现这一点,我只需要更改 createContextual(...)
方法中的一些代码。我改变了这个:
// ==== Determine target type =====
final Class<? extends StringConvertible> targetClass;
JavaType type = beanProperty.getType(); // -> beanProperty is null when the StringConvertible type is a root value
targetClass = (Class<? extends StringConvertible>) type.getRawClass();
对此:
// ==== Determine target type =====
final Class<? extends StringConvertible> targetClass;
{
// ==== Get the contextual type info =====
final JavaType type;
if (beanProperty != null)
type = beanProperty.getType(); // -> beanProperty is null when the StringConvertible type is a root value
else {
type = deserializationContext.getContextualType();
}
// ==== Get raw Class from type info =====
targetClass = (Class<? extends StringConvertible>) type.getRawClass();
}
关于java - 如何将 Jackson 的 ContextualDeserializer 用于根值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31300046/