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);
    }
}