将 JSON 反序列化为泛型类型的记录

问题描述 投票:0回答:1

我有一个服务返回 JSON 并具有“稳定”响应,但包含可能会有所不同的“投影”字段。示例:

{
  "name": "test",
  "projection": {
    "id": 1,
    "description": "Hey there",
    "blurb": "This is a test"
  }
}

我想反序列化为记录并提供一种机制,人们可以为已返回的投影定义自己的记录。这就是我想出的,一个带有实现

Response
接口的通用成员的
Projection
记录。

package com.example.json

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import java.io.IOException;

public class Example {
  private static final ObjectMapper MAPPER =
      new ObjectMapper()
          .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
          .configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true)
          .registerModule(new Jdk8Module());

  private static final String SAMPLE_JSON =
      "{\"name\":\"test\", \"projection\": {\"id\": 1, \"description\": \"Hey there\", \"blurb\": \"This is a test\"}}";

  record Response<T extends Projection>(String name, T projection) {}

  interface Projection {
    int id();

    static <T extends Projection> Response<T> parse(byte[] bytes) {
      try {
        TypeReference<Response<T>> typeReference = new TypeReference<>() {};
        return MAPPER.readValue(bytes, typeReference);  // Explodes here
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
  }

  record Projection1(int id, String description, String blurb) implements Projection {}

  public static void main(String[] args) {
    Response<Projection1> response = Projection.<Projection1>parse(SAMPLE_JSON.getBytes());

    System.out.println(response);
  }
}

此操作失败并出现错误:

Exception in thread "main" java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.example.json.Example$Projection` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 31] (through reference chain: com.example.json.Example$Response["projection"])
    at com.example.json.Example$Projection.parse(Example.java:29)
    at com.example.json.Example.main(Example.java:37)

我预计预测会有相当多的变化。有没有一种方法可以让我将解析逻辑保留在界面/一个地方,并避免例如在每个具体的 Projection 实现上实现 parse 方法,这可以访问具体类型?

java json jackson
1个回答
1
投票

通过将

TypeReference
添加到
Projection::parse
签名来使其发挥作用。

  interface Projection {
    int id();

    static <T extends Projection> Response<T> parse(byte[] bytes, TypeReference<Response<T>> typeReference) {
      try {
        return MAPPER.readValue(bytes, typeReference);
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

然后调用时传递:

  public static void main(String[] args) {
    Response<Projection1> response = Projection.parse(SAMPLE_JSON.getBytes(), new TypeReference<>() {});
    System.out.println(response);
  }

理想情况下,我不想通过

TypeReference
,但这可行。

© www.soinside.com 2019 - 2024. All rights reserved.