近期在工作中踩到的坑,返回结果使用Result<T>
封装,结果踩了两个FastJson与构造方法的大坑,分享下,注意别踩到相同的坑。
Result<String>
对象,序列化为JSON字符串Result<String>
对象 public static void main(String[] args) {
// 创建对象
Result<String> result = new Result<String>()
.setRet(-1)
.setData(null)
.setError(new Result.Error(404).addErrorDetail("id", 9, "resource.not.found"));
// 将对象序列化为JSON字符串
String resultJson = JSONObject.toJSONString(result,
SerializerFeature.WriteMapNullValue,
SerializerFeature.PrettyFormat);
System.out.println(resultJson);
// 反序列化
Result<String> resultFromJson = JSON.parseObject(resultJson, new TypeReference<Result<String>>() {
});
System.out.println(resultFromJson);
}
序列化后的JSON字符串
{
"data":null,
"error":{
"code":404,
"details":[
{
"data":9,
"field":"id",
"msg":"resource.not.found"
}
]
},
"ret":-1
}
和构造方法有关。creatorConstructor
被确定为Error(int code)
这个构造方法,而这个构造方法就只有int code
这个一个参数,在反序列化创建Result.Error
时,就这完成了code
字段的赋值,而没有反序列化details
字段。
解决方案
Result.Error
增加构造方法Error(int code, List<ErrorDetail> details)
Result<T>增加无参构造方法
正确的Result<T>
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.ArrayList;
import java.util.List;
@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) {
this.code = code;
this.details = new ArrayList<>();
}
/**
* 特别注意,改构造方法千万不要放到上一个构造方法前,否则会导致FastJson反序列出问题。
* <p>
* public Error(CmnCode code) {
* this.code = code.getCode();
* this.details = new ArrayList<>();
* }
*/
public Error(int code, List<ErrorDetail> details) {
this.code = code;
if (null == details) {
this.details = new ArrayList<>();
} else {
this.details = details;
}
}
public Error addErrorDetail(String msg) {
this.details.add(new Result.ErrorDetail()
.setMsg(msg));
return this;
}
public Error addErrorDetail(String field, String msg) {
this.details.add(new Result.ErrorDetail()
.setField(field)
.setMsg(msg));
return this;
}
public Error addErrorDetail(Object data, String msg) {
this.details.add(new Result.ErrorDetail()
.setData(data)
.setMsg(msg));
return this;
}
public Error addErrorDetail(String field, Object data, String msg) {
this.details.add(new Result.ErrorDetail()
.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 java.io.Serializable;
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) {
this.code = code;
}
public static CmnCode fromHttpStatus(int httpStatus) {
for (CmnCode cmnCode : values()) {
if (cmnCode.getCode() == httpStatus) {
return cmnCode;
}
}
return INTERNAL_ERROR;
}
public int getCode() {
return code;
}
public CmnCode setCode(int code) {
this.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) {
this.code = cmnCode.getCode();
this.details = new ArrayList<>();
}
public Error(int code) {
this.code = code;
this.details = new ArrayList<>();
}
}
}