近期在工作中踩到的坑,返回结果使用Result<T>
封装,结果踩了两个FastJson与构造方法的大坑,分享下,注意别踩到相同的坑。
1. 测试代码
- 创建了
Result<String>
对象,序列化为JSON字符串 - 将JSON字符串反序列化为
Result<String>
对象public static void main(String[] args) { // 创建对象 Result<String> result = new Result<String>() .setRet(-1) .setData(null) .setError(new (404).addErrorDetail("id", 9, "")); // 将对象序列化为JSON字符串 String resultJson = (result, , ); (resultJson); // 反序列化 Result<String> resultFromJson = (resultJson, new TypeReference<Result<String>>() { }); (resultFromJson); }
序列化后的JSON字符串
{ "data":null, "error":{ "code":404, "details":[ { "data":9, "field":"id", "msg":"" } ] }, "ret":-1 }
和构造方法有关。
creatorConstructor
被确定为Error(int code)
这个构造方法,而这个构造方法就只有int code
这个一个参数,在反序列化创建时,就这完成了
code
字段的赋值,而没有反序列化details
字段。
解决方案
-
增加构造方法
Error(int code, List<ErrorDetail> details)
-
Result<T>增加无参构造方法
至此,问题解决。
小结
- JSON反序列化与构造方法密切相关,推荐每个类都提供无参构造方法
- 如果某个类从设计上不适合提供无参构造方法,需要特别注意以上两个问题。推荐提供全参构造方法。
相关类定义
正确的Result<T>
import ;
import ;
import ;
import ;
@Data
@Accessors(chain = true)
public class Result<T> {
/**
* ret >= 0 success;
* ret < 0 error;
*/
private int ret;
private T data;
private Error error;
@Data
@Accessors(chain = true)
public static class Error {
private int code;
private List<ErrorDetail> details;
public Error(int code) {
= code;
= new ArrayList<>();
}
/**
* 特别注意,改构造方法千万不要放到上一个构造方法前,否则会导致FastJson反序列出问题。
* <p>
* public Error(CmnCode code) {
* = ();
* = new ArrayList<>();
* }
*/
public Error(int code, List<ErrorDetail> details) {
= code;
if (null == details) {
= new ArrayList<>();
} else {
= details;
}
}
public Error addErrorDetail(String msg) {
(new ()
.setMsg(msg));
return this;
}
public Error addErrorDetail(String field, String msg) {
(new ()
.setField(field)
.setMsg(msg));
return this;
}
public Error addErrorDetail(Object data, String msg) {
(new ()
.setData(data)
.setMsg(msg));
return this;
}
public Error addErrorDetail(String field, Object data, String msg) {
(new ()
.setField(field)
.setData(data)
.setMsg(msg));
return this;
}
}
@Data
@Accessors(chain = true)
public static class ErrorDetail {
/**
* error field;
*/
private String field;
/**
* error field value;
*/
private Object data;
/**
* error field message;
*/
private String msg;
}
}
CmnCode枚举
import ;
public enum CmnCode implements Serializable {
/**
* ok
*/
OK(200),
BAD_REQUEST(400),
UNAUTHORIZED(401),
FORBIDDEN(403),
NOT_FOUND(404),
METHOD_NOT_ALLOWED(405),
NOT_ACCEPTABLE(406),
CONFLICT(409),
PRECONDITION_FAILED(412),
UNSUPPORTED_MEDIA_TYPE(415),
PROTOCOL_NOT_MATCH(444),
INTERNAL_ERROR(500),
GATEWAY_ERROR(502),
SERVICE_UNAVAILABLE(503),
GATEWAY_TIMEOUT(504);
private int code;
CmnCode(int code) {
= code;
}
public static CmnCode fromHttpStatus(int httpStatus) {
for (CmnCode cmnCode : values()) {
if (() == httpStatus) {
return cmnCode;
}
}
return INTERNAL_ERROR;
}
public int getCode() {
return code;
}
public CmnCode setCode(int code) {
= code;
return this;
}
}
引发问题的Result<T>
@Data
@Accessors(chain = true)
public class Result<T> {
/**
* ret >= 0 success;
* ret < 0 error;
*/
private int ret;
private T data;
private Error error;
@Data
@Accessors(chain = true)
public static class Error {
private int code;
private List<ErrorDetail> details;
public Error(CmnCode cmnCode) {
= ();
= new ArrayList<>();
}
public Error(int code) {
= code;
= new ArrayList<>();
}
}
}