Jackson 反序列化失败,class 包含具有特定类型的 Map
Jackson deserialize fails with class containing a Map with specific types
我有一个扩展 HashMap 的 class,它在另一个 class 中被引用,该 class 正在使用 Jackson(jackson-core 2.10)序列化为 JSON .5).
序列化结果为:
{"code":null,"translations":{"@class":"c...TranslationMap","nb_NO":"Minimum","en_US":"Minimum","nn_NO":"Minimum"}}
(class 包名称已删除)
但是当反序列化抛出异常时我一直无法弄清楚,如何解决:
Could not resolve type id 'c...TranslationMap' as a subtype of c...TranslationMap<java.util.Locale,java.lang.String>
: Not a subtype
奇怪的是,我在使用 DCEVM 的 JDK 时无法重现这个问题,但是当我切换到客户 JDK adopt-openjdk-11 时,我就开始 运行进入这个问题。
考虑到这个问题也发生在服务器上,我必须使用 adopt-openjdk-11 来解决这个问题。
有什么想法吗?
TranslationMap.java
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public class TranslationMap extends HashMap<Locale, String> {
...
// empty constructor
// some additional methods
}
Translation.java
public class Translation {
private String code;
private TranslationMap translations;
...
// empty constructor
// get / set methods
}
反序列化方法:
private Object deserialize(String value, String className) {
try {
var classType = Class.forName(className);
return new ObjectMapper().readValue(value, classType);
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
异常:
Caused by: com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'c...TranslationMap' as a subtype of `c...TranslationMap<java.util.Locale,java.lang.String>`: Not a subtype
at [Source: (String)"{"code":null,"translations":{"@class":"c...TranslationMap","nb_NO":"Minimum","en_US":"Minimum","nn_NO":"Minimum"}}"; line: 1, column: 39] (through reference chain: c...Translation["translations"])
at com.fasterxml.jackson.databind.exc.InvalidTypeIdException.from(InvalidTypeIdException.java:43)
at com.fasterxml.jackson.databind.DeserializationContext.invalidTypeIdException(DeserializationContext.java:1761)
at com.fasterxml.jackson.databind.DatabindContext._throwNotASubtype(DatabindContext.java:280)
at com.fasterxml.jackson.databind.DatabindContext.resolveAndValidateSubType(DatabindContext.java:241)
at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver._typeFromId(ClassNameIdResolver.java:72)
at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver.typeFromId(ClassNameIdResolver.java:66)
at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:156)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:113)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:97)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromAny(AsPropertyTypeDeserializer.java:193)
我不知道为什么,但是 Class.forName(className)
没有解析到对象映射器类型工厂中引用的同一个 Class 对象,而是似乎创建了另一个 Class相同 class 的对象。这就是为什么它认为类型相等。
我的解决方案是改用类型工厂的 class。
private Object deserialize(String value, String className) {
try {
var jackson = new ObjectMapper();
var classType = jackson.getTypeFactory().findClass(className));
return jackson.readValue(value, classType);
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
我有一个扩展 HashMap
{"code":null,"translations":{"@class":"c...TranslationMap","nb_NO":"Minimum","en_US":"Minimum","nn_NO":"Minimum"}}
(class 包名称已删除)
但是当反序列化抛出异常时我一直无法弄清楚,如何解决:
Could not resolve type id 'c...TranslationMap' as a subtype of
c...TranslationMap<java.util.Locale,java.lang.String>
: Not a subtype
奇怪的是,我在使用 DCEVM 的 JDK 时无法重现这个问题,但是当我切换到客户 JDK adopt-openjdk-11 时,我就开始 运行进入这个问题。 考虑到这个问题也发生在服务器上,我必须使用 adopt-openjdk-11 来解决这个问题。
有什么想法吗?
TranslationMap.java
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public class TranslationMap extends HashMap<Locale, String> {
...
// empty constructor
// some additional methods
}
Translation.java
public class Translation {
private String code;
private TranslationMap translations;
...
// empty constructor
// get / set methods
}
反序列化方法:
private Object deserialize(String value, String className) {
try {
var classType = Class.forName(className);
return new ObjectMapper().readValue(value, classType);
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
异常:
Caused by: com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'c...TranslationMap' as a subtype of `c...TranslationMap<java.util.Locale,java.lang.String>`: Not a subtype
at [Source: (String)"{"code":null,"translations":{"@class":"c...TranslationMap","nb_NO":"Minimum","en_US":"Minimum","nn_NO":"Minimum"}}"; line: 1, column: 39] (through reference chain: c...Translation["translations"])
at com.fasterxml.jackson.databind.exc.InvalidTypeIdException.from(InvalidTypeIdException.java:43)
at com.fasterxml.jackson.databind.DeserializationContext.invalidTypeIdException(DeserializationContext.java:1761)
at com.fasterxml.jackson.databind.DatabindContext._throwNotASubtype(DatabindContext.java:280)
at com.fasterxml.jackson.databind.DatabindContext.resolveAndValidateSubType(DatabindContext.java:241)
at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver._typeFromId(ClassNameIdResolver.java:72)
at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver.typeFromId(ClassNameIdResolver.java:66)
at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:156)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:113)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:97)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromAny(AsPropertyTypeDeserializer.java:193)
我不知道为什么,但是 Class.forName(className)
没有解析到对象映射器类型工厂中引用的同一个 Class 对象,而是似乎创建了另一个 Class相同 class 的对象。这就是为什么它认为类型相等。
我的解决方案是改用类型工厂的 class。
private Object deserialize(String value, String className) {
try {
var jackson = new ObjectMapper();
var classType = jackson.getTypeFactory().findClass(className));
return jackson.readValue(value, classType);
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}