Jackson 没有反序列化已序列化的通用列表
当使用 Apache Jersey 和 Jackson 进行 JSON 序列化(在服务器和客户端上)时,我在反序列化通用列表时遇到了问题.
我生成的 JSON 如下,data"中的所有 3 个类都实现了CheckStatusDetail":
<代码>{错误代码":0,错误消息":空,类型":数组",数据" : [ {"@class" : "com.rrr.base.status.module.dto.DiscoveryAgentCheckStatusDetail",服务器信息":{"@class" : "com.rrr.base.util.discovery.config.xml.XMLServerInfo",名称":java",位置":西奥",描述":sddgs",组":java",别名":[水星"]}}, {"@class" : "com.rrr.base.status.module.dto.MongoDBCheckStatusDetail",地址":[本地主机:27017"],版本":2.5",已连接":真}, {"@class" : "com.rrr.base.status.module.dto.NetworkCheckStatusDetail",裂脑":假}],计数":3,状态":0}
生成此 JSON 的对象如下所示,我在客户端使用相同的类:
公共类 NSResponse;实现可序列化{私有静态最终长序列版本UID = 1L;公共静态最终 int STATUS_OK = 0;公共静态最终 int STATUS_ERROR = -1;public static final String TYPE_OBJECT = "object";public static final String TYPE_ARRAY = "array";私有 int 状态;私人 int 错误代码;私有字符串错误消息;私有字符串类型;私人名单<T>数据;私人整数计数;公共 NSResponse() { }公共 NSResponse(int errorCode, String errorMessage) {this.status = STATUS_ERROR;this.errorCode = 错误代码;this.errorMessage = 错误消息;}公共 NSResponse(T 数据) {this.status = STATUS_OK;this.type = TYPE_OBJECT;this.data = new ArrayList();this.data.add(data);this.count = this.data.size();}公共 NSResponse(列出数据){this.status = STATUS_OK;this.type = TYPE_ARRAY;this.data = 数据;this.count = (data == null) ?0:数据大小();}/* 省略 getter 和 setter */}
自从我将此注释添加到我的 CheckStatusDetail 接口后,@class 信息正在被应用:
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")公共接口 CheckStatusDetail 扩展 Serializable {}
尝试在客户端使用 JSON 时,我收到此错误:
java.lang.ClassCastException: java.util.LinkedHashMap 无法转换为 com.rrr.base.status.module.dto.CheckStatusDetail
当我在反序列化后第一次尝试访问数据"字段时会发生此错误.如果我调试客户端,Jackson 似乎返回了一个 List<LinkedHashMap>,它解释了错误,因为我期待一个 List<CheckStatusDetail>.p>
我做错了什么?
解决方案您需要显示更多代码,特别是关于如何调用反序列化,但从错误中我猜您没有传递 T 的参数化.如果是缺少时,T 只能被假定为 Object 类型,并且 Object 的标称类型绑定到本机"Java 类型,对于 JSON 对象,它是 Map(特别是 LinkedHashMap 以保持顺序).
所以您可能只需要在反序列化时指定对象的通用类型(对于序列化,它不需要,因为运行时类型可用);通过使用 TypeReference(不是普通的 Class,因为它没有泛型类型信息),或者通过构造支持泛型的 JavaType.例如:
NSResponse<CheckStatusDetail>resp = mapper.readValue(json, new TypeReference<NSResponse<CheckStatusDetail>>() { });
或
NSResponse<CheckStatusDetail>resp = mapper.readValue(json, TypeFactory.genericType(NSResponse.class, CheckStatusDetails.class));
两者都有效;如果类型仅动态可用,则后者是必需的.
When using Apache Jersey with Jackson for JSON serialisation (on both server and client), I'm hitting a problem when deserialising a generic List.
The JSON I am producing is as follows, all 3 classes in "data" implement "CheckStatusDetail":
{
"errorCode" : 0,
"errorMessage" : null,
"type" : "array",
"data" : [ {
"@class" : "com.rrr.base.status.module.dto.DiscoveryAgentCheckStatusDetail",
"serverInfo" : {
"@class" : "com.rrr.base.util.discovery.config.xml.XMLServerInfo",
"name" : "java",
"location" : "THEO",
"description" : "sddgs",
"group" : "java",
"aliases" : [ "mercury" ]
}
}, {
"@class" : "com.rrr.base.status.module.dto.MongoDBCheckStatusDetail",
"addresses" : [ "localhost:27017" ],
"version" : "2.5",
"connected" : true
}, {
"@class" : "com.rrr.base.status.module.dto.NetworkCheckStatusDetail",
"splitBrain" : false
} ],
"count" : 3,
"status" : 0
}
The object that produces this JSON looks like this, I'm using the same class on the client side:
public class NSResponse<T> implements Serializable {
private static final long serialVersionUID = 1L;
public static final int STATUS_OK = 0;
public static final int STATUS_ERROR = -1;
public static final String TYPE_OBJECT = "object";
public static final String TYPE_ARRAY = "array";
private int status;
private int errorCode;
private String errorMessage;
private String type;
private List<T> data;
private int count;
public NSResponse() { }
public NSResponse(int errorCode, String errorMessage) {
this.status = STATUS_ERROR;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public NSResponse(T data) {
this.status = STATUS_OK;
this.type = TYPE_OBJECT;
this.data = new ArrayList<T>();
this.data.add(data);
this.count = this.data.size();
}
public NSResponse(List<T> data) {
this.status = STATUS_OK;
this.type = TYPE_ARRAY;
this.data = data;
this.count = (data == null) ? 0 : data.size();
}
/* Getters and setters omitted */
}
The @class information is being applied since I added this annotation to my CheckStatusDetail interface:
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
public interface CheckStatusDetail extends Serializable {}
When trying to consume the JSON at the client end, I get this error:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.rrr.base.status.module.dto.CheckStatusDetail
This error occurs the first time I try to access the "data" field after deserialising it. If I debug the client end, Jackson seems to be returning a List<LinkedHashMap>, which explains the error, since I'm expecting a List<CheckStatusDetail>.
What am I doing wrong?
解决方案You need to show bit more code, specifically on how you invoke deserialization, but from the error I would guess you are not passing parameterization of T. If it is missing, T can only be assumed to be of type Object, and nominal type of Object is bound to "native" Java type, which for JSON objects is Map (and specifically, LinkedHashMap to preserve order).
So you probably just need to specify generic type of object on deserialization (for serialization it is not needed as runtime type is available); either by using TypeReference (not plain Class, since that has no generic type info), or by constructing generic-enabled JavaType. For example:
NSResponse<CheckStatusDetail> resp = mapper.readValue(json, new TypeReference<NSResponse<CheckStatusDetail>>() { });
or
NSResponse<CheckStatusDetail> resp = mapper.readValue(json, TypeFactory.genericType(NSResponse.class, CheckStatusDetails.class));
both work; latter is necessary if type is only dynamically available.
相关文章