如何配置Jackson以默认键入反序列化命名类型?
请考虑以下示例:
package com.example; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; public class JacksonDeserializationOfNamedTypes { public static void main(String[] args) throws Exception { ObjectMapper jackson = new ObjectMapper(); jackson.enableDefaultTypingAsProperty(DefaultTyping.JAVA_LANG_OBJECT, "@type"); Balloon redBalloon = new Balloon("red"); String json = jackson.writeValueAsString(redBalloon); //{"@type":"Balloon","color":"red"} //assume the JSON could be anything Object deserialized = jackson.readValue(json, Object.class); assert deserialized instanceof Balloon; assert redBalloon.equals(deserialized); } @JsonTypeName("Balloon") @JsonTypeInfo(use = Id.NAME) public static final class Balloon { private final String color; //for deserialization private Balloon() { this.color = null; } public Balloon(final String color) { this.color = color; } public String getColor() { return color; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; final Balloon other = (Balloon) obj; return this.color.equals(other.color); } @Override public int hashCode() { int result = color.hashCode(); result = 31 * result + color.hashCode(); return result; } @Override public String toString() { return color + " balloon"; } } }
反序列化在运行时失败,出现以下exception: Exception in thread "main" java.lang.IllegalArgumentException: Invalid type id 'Balloon' (for id type 'Id.class'): no such class found
生成的JSON肯定具有Jackson正确确定类型所需的所有信息,因此如何配置ObjectMapper以将"Balloon"
正确映射到com.example.JacksonDeserializationOfNamedTypes$Balloon
?
我当前的解决方案涉及自定义反序列化器和手动形成的类型名称映射到Java类型的组合:
package com.example.jackson; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; public class JacksonDeserializerOfNamedTypes extends StdDeserializer
生成的JSON肯定具有Jackson正确确定类型所需的所有信息
您已向jackson提供以下内容
Object deserialized = jackson.readValue(json, Object.class);
Object
是所有Java引用类型的超类型。 这是众所周知的。 对jackson不是很有用。 您的JSON还包含
{"@type":"Balloon","color":"red"}
鉴于此并感谢
jackson.enableDefaultTypingAsProperty(DefaultTyping.JAVA_LANG_OBJECT, "@type");
jackson可以推断它可以使用@type
元素的值。 但是, Balloon
这个名字怎么办呢? jackson不知道类路径上的所有类型。 您是否拥有完全限定名称Balloon
的类型? 你有一个名为com.example.Balloon
的类型或一个名为org.company.toys.Balloon
的类型吗? jackson应该如何选择?
@JsonTypeInfo
和注释族通常用于inheritance层次结构的反序列化。 例如
@JsonTypeInfo(use = Id.NAME) @JsonSubTypes(value = @Type(value = Balloon.class)) public abstract static class Toy { } @JsonTypeName("Balloon") public static final class Balloon extends Toy {
和
Object deserialized = jackson.readValue(json, Toy.class);
现在Jackson可以查找Toy
类及其元数据,这些元数据将其子类标识为Balloon
,它也可以检查名称Balloon
。
如果您没有尝试为inheritance层次结构建模,那么最简单的解决方案是在@JsonTypeName
注释中使用Balloon
类的完全限定名称。
@JsonTypeName("com.example.JacksonDeserializationOfNamedTypes$Balloon") @JsonTypeInfo(use = Id.NAME) public static final class Balloon {
然后该名称将显示在JSON中,Jackson将使用它来确定目标类。
总之(太晚了:))
你应该:
-
提供完全限定的className,以便jackson可以找到要反序列化的类,例如:
@JsonTypeName("com.example.JacksonDeserializationOfNamedTypes$Balloon")
-
或添加自定义反序列化器来处理您的
Balloon
类型