【问题标题】:Jackson: Deserializing a polymorphic listJackson:反序列化多态列表
【发布时间】:2021-11-08 03:45:27
【问题描述】:

我想为我们的 REST API 实现一个自定义的反序列化器,它不仅被 Java 应用程序使用。因此我不想让 Jackson 将类型信息放入序列化的 JSON 中。

我目前正在努力反序列化 CollectionExpand,因为它包含特定 ResourceModel 的列表 data

public class EntityModel<R extends ResourceModel> implements Serializable {

  private R data;
  private List<ResourceLink> links;
  private List<CollectionExpand> expands;
}

public class CollectionExpand {

  private String name;
  // Resource Model is an interface
  private Collection<ResourceModel> data;
}

ResourceModel 是一个接口,每个CollectionExpand 包含一个ResourceModel 每个name 类型的集合。

例如,json 输出可能如下所示。

{
    "data": {},
    "links": [],
    "expand": [
        {
            "name": "photos",
            "data": [
                {
                    "id": 12,
                    "name": "hello.jpg"
                },
                {
                    "id": 12,
                    "name": "hello.jpg"
                }
            ]
        },
        {
            "name": "persons",
            "data": [
                {
                    "id": 783378,
                    "name": "Peter",
                    "age": 12
                },
                {
                    "id": 273872,
                    "name": "Maria",
                    "age": 77
                }
            ]
        }
    ]
}

如您所见,每个名称都包含相同类型的资源模型。 photos 包含 PhotoResourceModelperson 包含 PersonResourceModel

我开始实现我的自定义 Jackson Deserializer

public class CollectionExpandDeserializer extends StdDeserializer<CollectionExpand> {

  public CollectionExpandDeserializer() {
    super(CollectionExpand.class);
  }

  @Override
  public CollectionExpand deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    CollectionExpand collectionExpand = new CollectionExpand();

    if (Objects.equals(p.nextFieldName(), "name")) {
      collectionExpand.setName(p.nextTextValue());
    }

    if (Objects.equals(p.nextFieldName(), "data")) {
       // depending on the field name I would like to delegate the deserialization to a specific type.


       if (name.equals("photos") {
         // how to do this?
         collectionExpand.setData(/* deserialize to a list of PhotoResource */);
       }

    }


    return collectionExpand;
  }

我目前不知道如何委派告诉 Jackson 将其反序列化为 PhotoResource 列表。

一般来说,这是正确的方法还是有其他方法(在序列化时不将任何 Jackson 元数据放入 JSON)?

【问题讨论】:

  • 我在使用 C# 的项目中遇到了类似的问题。您可以使用的另一种解决方法(我知道它值得畏惧,但它有效)是定义一个自定义类型,将每个具体类型存储为一个单独的集合,您可以定义映射器方法,让您在两种形式之间进行转换。此方法依赖于类型匹配。
  • @OmarAbdelBari 感谢您的评论。问题是我需要将数据反序列化回现有类。没有机会改变结构。

标签: java jackson deserialization json-deserialization


【解决方案1】:

我最终实现了我的自定义反序列化器,如下所示

@Override
public CollectionExpand deserialize(JsonParser p, DeserializationContext ctx) throws IOException {

    JsonNode node = ctx.readTree(p);

    CollectionExpand collectionExpand = new CollectionExpand();

    collectionExpand.setName(node.get("name").asText());

    ArrayNode data = node.withArray("data");

    Iterator<JsonNode> iterator = data.iterator();
    while (iterator.hasNext()) {

      Class<? extends ResourceModel> aClass = resolveClass(collectionExpand.getName());

      if (aClass != null) {
        JsonNode jsonNode = iterator.next();
        collectionExpand.getData().add(p.getCodec().treeToValue(jsonNode, aClass));
      }
    }
    return collectionExpand;
}

private Class<? extends ResourceModel> resolveClass(String name) {
    if ("contents".equals(name)) {
      return ContentsGetResourceModel.class;
    } else if ("tags".equals(name)) {
      return TagsGetResourceModel.class;
    } else {
      return null;
    }
}

我花了一些时间来了解如何将 JsonNode/TreeNode 反序列化为特定类型。最后我了解到,这基本上可以通过使用解析器编解码器来完成。

PhotoResource photoResource = p.getCodec().treeToValue(jsonNode, PhotoResource.class);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-01
    • 2023-03-18
    • 2016-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-30
    • 1970-01-01
    相关资源
    最近更新 更多