使用 Jackson 进行动态多态类型处理

2022-09-04 22:59:04

我有一个类似于下面的类层次结构:

public static class BaseConfiguration {
}

public abstract class Base {
  private BaseConfiguration configuration;
  public String id;

  public BaseConfiguration getConfiguration() { ... }
  public void setConfiguration(BaseConfiguration configuration) { ... }
}

public class A extends Base {
   public static class CustomConfigurationA extends BaseConfiguration {
       String filename;
       String encoding;
   }

   CustomConfigurationA getConfiguration() { ... }
}

class B extends Base {
   public static class CustomConfigurationB extends BaseConfiguration {
       /* ... */
   }

   CustomConfigurationB getConfiguration() { ... }
}

和像这样的json输入(我自己无法更改)

{
    "id":"File1",
    "configuration":{
         "filename":"...",
         "encoding":"UTF-8"
     }
}

我正在像这样用Jackson解析Java中的JSON

ObjectMapper mapper = new ObjectMapper();
value = mapper.readValue(in, nodeType);

我想使用JAVA / Jackson从JSON反序列化类A,B和其他类。JSON 中没有嵌入类型信息(也不能)。我不能在类上使用注释(我不拥有它们),我(相信)我不能使用mixins,因为像A和B这样的类可能有任意数量的(并且mixins不是动态的)。好消息是,反序列化代码知道哪个是用于反序列化的正确自定义类(基本上有一个已知的从类到配置类的映射),但我不知道在反序列化JSON时如何让Jackson识别此信息。

简而言之:我希望能够通过在ObjectMapper上设置任何必要的内容来解析配置对象的反序列化类型,具体取决于周围的类类型。如何实现这一点?


答案 1

显然,答案是实现类似于 http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html 发布的第六个解决方案,该解决方案使用唯一的JSON元素名称来标识要反序列化的目标类型。


答案 2

程序员布鲁斯提供的好答案!

我有一个多态性案例,其中我想将域对象保留为POJO,而不是使用对Jackson注释的依赖关系。

因此,我倾向于使用自定义反序列化程序和工厂来决定类型或初始化具体类。

这是我的代码...(请注意,我有一个注释层次结构,实际上是“用户标签”而不是Java注释)

下面是反序列化方法

public class AnnotationDeserializer extends StdDeserializer<Annotation> {

AnnotationDeserializer() {
    super(Annotation.class);
}

@Override
public Annotation deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {

    ObjectMapper mapper = (ObjectMapper) jp.getCodec();
    ObjectNode root = (ObjectNode) mapper.readTree(jp);
    Class<? extends Annotation> realClass = null;

    Iterator<Entry<String, JsonNode>> elementsIterator = root.getFields();
    while (elementsIterator.hasNext()) {
        Entry<String, JsonNode> element = elementsIterator.next();
        if ("type".equals(element.getKey())) {
            realClass = AnnotationObjectFactory.getInstance()
                    .getAnnotationClass(element.getKey());
            break;
        }
    }

    if (realClass == null)
        return null;
    return mapper.readValue(root, realClass);
}
}