我有一个服务返回 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 方法,这可以访问具体类型?
通过将
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
,但这可行。