我正在为一个有点复杂的API构建一个客户端库。但是,不同的响应对象之间存在显着的共性,如下所示:
{
"response": "Success",
"delay": 0.241,
"time": 125425234,
"message": null,
"a": "Payloads"
}
{
"response": "AuthFailure",
"delay": 0.112,
"time": 1324515123,
"message": "Wrong password",
"b": 1234
}
{
"response": "Success",
"delay": 0.294,
"time": 12461246123,
"message": null,
"c": True
"d": 245.1
}
我想要分解公共部分,并希望将它们反序列化为对象组合:
Response<AData>
Response<BData>
Response<CDData>
(类定义看起来像):
class Response<T> {
final Response response;
final Double delay;
final Timestamp time;
final String message;
final T inner;
...
}
class AData {
final String a;
...
}
class BData {
final int b;
...
}
class BData {
final bool c;
final double d;
...
}
这很像“JsonUnwrapped”杰克逊注释的倒数。继承也会奏效。
不幸的是,我无法找到一种方法在Jackson中以合理的方式完成这一操作,而这种方式与ObjectMapper系统的其余部分相结合,而无需编写重要的附加模块。我错过了什么吗?是否有更好的方法来做这类事情?
答案 0 :(得分:1)
这里的问题是你(或杰克逊)需要知道需要用什么对象来转换请求。有两种方法:
1)使用继承。由于Jackson会为您处理所有事情,这种方法更加强大,但这种方法需要添加一个标记,杰克逊将使用该标记来选择应该用于转换的对象类型。不确定您是否可以添加这些标记,但下面的代码可以让您了解如何完成这些标记。 它非常简单 - 你只需要添加@JsonTypeInfo来配置哪个字段将用作标记,@ jsonSubTypes来定义所有可用于转换响应的类。
class ResponseA extends BaseResponse {
private String a;
}
class ResponseB extends BaseResponse {
private String b;
}
class ResponseCD extends BaseResponse {
private boolean c;
private double d;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = ResponseA.class, name = "a"),
@JsonSubTypes.Type(value = ResponseB.class, name = "b"),
@JsonSubTypes.Type(value = ResponseCD.class, name = "cd")
})
class BaseResponse {
private String response;
private double delay;
private long time;
private String message;
}
public class DynamicResponseInheritance {
private static final String RESPONSE_A = "{\n" +
" \"type\": \"a\",\n" +
" \"response\": \"Success\",\n" +
" \"delay\": 0.241,\n" +
" \"time\": 125425234,\n" +
" \"message\": null,\n" +
" \"a\": \"Payloads\"\n" +
"}";
private static final String RESPONSE_B = "{\n" +
" \"type\": \"b\",\n" +
" \"response\": \"AuthFailure\",\n" +
" \"delay\": 0.112,\n" +
" \"time\": 1324515123,\n" +
" \"message\": \"Wrong password\",\n" +
" \"b\": 1234\n" +
"}";
private static final String RESPONSE_CD = "{\n" +
" \"type\": \"cd\",\n" +
" \"response\": \"Success\",\n" +
" \"delay\": 0.294,\n" +
" \"time\": 12461246123,\n" +
" \"message\": null,\n" +
" \"c\": true,\n" +
" \"d\": 245.1\n" +
"}";
public static void main(String []args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
BaseResponse responseA = objectMapper.readValue(RESPONSE_A, BaseResponse.class);
BaseResponse responseB = objectMapper.readValue(RESPONSE_B, BaseResponse.class);
BaseResponse responseCD = objectMapper.readValue(RESPONSE_CD, BaseResponse.class);
System.out.println(responseA);
System.out.println(responseB);
System.out.println(responseCD);
}
}
2)实施自定义反序列化程序。它也很简单,但在这种情况下,如果需要添加新类,则需要更新反序列化器。这种方法的好处是您不需要修改响应。
class ResponseDeserializer extends JsonDeserializer<BaseResponse> {
@Override
public BaseResponse deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
JsonNode root = parser.getCodec().readTree(parser);
JsonNode a = root.get("a");
if (a != null) {
String content = root.toString();
return mapper.readValue(content, ResponseA.class);
}
JsonNode b = root.get("b");
if (b != null) {
String content = root.toString();
return mapper.readValue(content, ResponseB.class);
}
JsonNode c = root.get("c");
if (c != null) {
String content = root.toString();
return mapper.readValue(content, ResponseCD.class);
}
return null;
}
}
class ResponseA extends BaseResponse {
private String a;
}
class ResponseB extends BaseResponse {
private String b;
}
class ResponseCD extends BaseResponse {
private boolean c;
private double d;
}
class BaseResponse {
private String response;
private double delay;
private long time;
private String message;
}
public class DynamicResponseCustomDeserializer {
private static final String RESPONSE_A = "{\n" +
" \"response\": \"Success\",\n" +
" \"delay\": 0.241,\n" +
" \"time\": 125425234,\n" +
" \"message\": null,\n" +
" \"a\": \"Payloads\"\n" +
"}";
private static final String RESPONSE_B = "{\n" +
" \"response\": \"AuthFailure\",\n" +
" \"delay\": 0.112,\n" +
" \"time\": 1324515123,\n" +
" \"message\": \"Wrong password\",\n" +
" \"b\": 1234\n" +
"}";
private static final String RESPONSE_CD = "{\n" +
" \"response\": \"Success\",\n" +
" \"delay\": 0.294,\n" +
" \"time\": 12461246123,\n" +
" \"message\": null,\n" +
" \"c\": true,\n" +
" \"d\": 245.1\n" +
"}";
public static void main(String []args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(BaseResponse.class, new ResponseDeserializer());
objectMapper.registerModule(module);
BaseResponse responseA = objectMapper.readValue(RESPONSE_A, BaseResponse.class);
BaseResponse responseB = objectMapper.readValue(RESPONSE_B, BaseResponse.class);
BaseResponse responseCD = objectMapper.readValue(RESPONSE_CD, BaseResponse.class);
System.out.println(responseA);
System.out.println(responseB);
System.out.println(responseCD);
}
}
答案 1 :(得分:0)
这不是@JsonUnwrapped
的倒数。这个 IS @JsonUnwrapped
,因为json指定了Response内部字段内的变量。
你想要的是告诉杰克逊你的数据的通用结构是什么,你可以用杰克逊的内部类型系统来实现这个目标:
这是我使用的类定义:
public class Response<T> {
public String response;
public Double delay;
public Timestamp time;
public String message;
@JsonUnwrapped
public T inner;
}
public class AData {
public String a;
}
public class BData {
public int b;
}
public class CData {
public boolean c;
public double d;
}
这就是你告诉杰克逊的通用结构:
ObjectMapper mapper = new ObjectMapper();
TypeFactory f = mapper.getTypeFactory();
JavaType responseAData = f.constructParametrizedType(Response.class, Response.class, AData.class);
然后反序列化可以通常的方式进行:
try (InputStream is = new FileInputStream("C://Temp/xx.json")) {
Response<AData> r = (Response<AData>) mapper.readValue(is, responseAData);
System.out.println(r.inner.a );
} catch (Exception e) {
e.printStackTrace();
}
根据问题指定输入,我输出为Payloads
这里的狡猾警告是,您的客户需要事先知道响应的类型