你也遇到JSONException:create instance error, null...问题啦?

近期在工作中踩到的坑,返回结果使用Result封装,结果踩了两个FastJson与构造方法的大坑,分享下,注意别踩到相同的坑。

1. 测试代码

  1. 创建了Result对象,序列化为JSON字符串
  2. 将JSON字符串反序列化为Result对象
    public static void main(String[] args) {
            // 创建对象
            Result result = new Result()
                    .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 resultFromJson = JSON.parseObject(resultJson, new TypeReference>() {
            });
            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 details)
  • Result增加无参构造方法
    至此,问题解决。

小结

  • JSON反序列化与构造方法密切相关,推荐每个类都提供无参构造方法
  • 如果某个类从设计上不适合提供无参构造方法,需要特别注意以上两个问题。推荐提供全参构造方法。

相关类定义

正确的Result

import lombok.Data;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.List;


@Data
@Accessors(chain = true)
public class Result {

    /**
     * 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 details;

        public Error(int code) {
            this.code = code;
            this.details = new ArrayList<>();
        }

        /**
         * 特别注意,改构造方法千万不要放到上一个构造方法前,否则会导致FastJson反序列出问题。
         * 

* public Error(CmnCode code) { * this.code = code.getCode(); * this.details = new ArrayList<>(); * } */ public Error(int code, List 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

@Data
@Accessors(chain = true)
public class Result {

    /**
     * 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 details;

        public Error(CmnCode cmnCode) {
            this.code = cmnCode.getCode();
            this.details = new ArrayList<>();
        }

        public Error(int code) {
            this.code = code;
            this.details = new ArrayList<>();
        }
    }
}

你可能感兴趣的:(踩坑记,postman,测试工具,json)