杰克逊,如何使用原始类型将 json 转换为 Java class?

Jackson, how to transform json to Java class using raw type?

我正在开发一个库,以便能够以更流畅的方式为个人项目查询 public api。我在 Jackson 将 API 答案转换为我的 java 对象时遇到了一些困难。

这是我的问题:

我使用 jdk 11 HttpClient 和 BodyHandlers.ofString() 调用此 API。到目前为止,没有什么很复杂的。但是当我想将 json 转换为 java 对象时,答案的格式给我带来了困难。

这是 Json 中的答案:

{
    "data": {
        "iso": "2021-02-04T20:35:21Z",
        "epoch": 1612470921
    }
}

但“数据”类型也可以出现在不同字段的其他答案中。例如:

{
    "data": {
        "base": "BTC",
        "currency": "USD",
        "amount": "37721.43"
    }
}

所以我决定创建一个像这样的通用数据类型:

public class DataDto<T> {

  T data;

  @JsonCreator
  public DataDto(@JsonProperty(value = "data") T data) {
    this.data = data;
  }
}

所以,让我们以第一个答案为例。 我还有一个名为 Time 的对象:

public class TimeDto {

  private LocalDateTime iso;
  private long epoch;

  @JsonCreator
  public TimeDto(
      @JsonProperty(value = "iso") LocalDateTime iso, @JsonProperty(value = "epoch") long epoch){
    this.iso = iso;
    this.epoch = epoch;
  }
}

还有我的函数,我想将答案转换为我的 java 对象:

public static Time getTime(final MyClient client) {
    var request =
        HttpRequest.newBuilder()
            .GET()
            .uri(
                URI.create(
                    client.getProperties().getApiUrl() + client.getProperties().getTimePath()))
            .header(ACCEPT, ACCEPT_VALUE)
            .build();

    try {
      var response = client.getClient().send(request, HttpResponse.BodyHandlers.ofString());

      var dataDtoType = client.getObjectMapper().getTypeFactory().constructType(DataDto.class);

      var timeDtoType =
          client
              .getObjectMapper()
              .getTypeFactory()
              .constructParametricType(TimeDto.class, dataDtoType);

      var toto = client.getJsonDeserializer().readValue(response.body(), timeDtoType);
      return null;
    } catch (Exception e) {
      log.error("An error occured while getting time", e);
      throw new MyException("An error occured while getting time", e);
    }
  }

如您所见,我尝试创建一个特定的 Jackson JavaType 以便能够以通用方式转换数据...但就我而言,当我测试此代码时,此异常附加:

java.lang.IllegalArgumentException: Cannot create TypeBindings for class com.github.badpop.mylib.client.dto.TimeDto with 1 type parameter: class expects 0
    at com.fasterxml.jackson.databind.type.TypeBindings.create(TypeBindings.java:126)
    at com.fasterxml.jackson.databind.type.TypeBindings.create(TypeBindings.java:96)
    at com.fasterxml.jackson.databind.type.TypeFactory.constructParametricType(TypeFactory.java:1109)
    at com.github.badpop.mylib.client.PublicResourcesService.getTime(PublicResourcesService.java:42)
    at com.github.badpop.mylib.client.MyClient.getTime(MyClient.java:70)
    at com.github.badpop.mylib.Main.main(Main.java:20)

如果您有什么想法可以帮助我解决我的问题,在此先感谢您!

最简单的是使用 TypeReference,例如

String json = "{\r\n" + 
              "    \"data\": {\r\n" + 
              "        \"iso\": \"2021-02-04T20:35:21Z\",\r\n" + 
              "        \"epoch\": 1612470921\r\n" + 
              "    }\r\n" + 
              "}";
ObjectMapper mapper = new ObjectMapper()
        .registerModule(new JavaTimeModule());

DataDto<TimeDto> data = mapper.readValue(json, new TypeReference<DataDto<TimeDto>>() {});

System.out.println("iso: " + data.getData().getIso());
System.out.println("epoch: " + data.getData().getEpoch());

您甚至可以“跳过”DataDto 部分:

TimeDto time = mapper.readValue(json, new TypeReference<DataDto<TimeDto>>() {}).getData();

System.out.println("iso: " + time.getIso());
System.out.println("epoch: " + time.getEpoch());

输出

iso: 2021-02-04T20:35:21Z
epoch: 1612470921

DTO

class DataDto<T> {
    private final T data;

    @JsonCreator
    public DataDto(@JsonProperty(value = "data") T data) {
        this.data = data;
    }

    public T getData() {
        return this.data;
    }
}
class TimeDto {
    private final Instant iso;
    private final long epoch;

    @JsonCreator
    public TimeDto(@JsonProperty(value = "iso") Instant iso,
                   @JsonProperty(value = "epoch") long epoch){
        this.iso = iso;
        this.epoch = epoch;
    }

    public Instant getIso() {
        return this.iso;
    }

    public long getEpoch() {
        return this.epoch;
    }
}