统一响应Json对象多了个success属性

统一响应Json对象多了个success属性

新项目,创建了一个统一响应的对象,有一个 isSuccess()的方法判断是否成功,然后发现响应结果中多了个 success属性。

然后在 Controller 层把要响应的数据直接 JSON 序列化输出,发出确实多了 success属性,可以定位是 JSON 序列化出了问题。

统一响应对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Data
public class ResponseModel<T> {

public static final int SUCCESS_CODE = HttpStatus.OK.value();

/**
* 响应码
*/
private int code = SUCCESS_CODE;

/**
* 响应消息
*/
private String message = "success";

/**
* 业务数据
*/
private T data;

public boolean isSuccess() {
return this.code == SUCCESS_CODE;
}
}

原因分析

响应数据使用的是 FastJson 序列化,Debug 断点进入,看到多了个 success 属性,而 ResponseModel 没有这个属性,但有一个 isSuccess 方法,可以确定两者是有关系的。

查看 JavaBeans(TM) Specification对 Bean 的 setter/getter 方法的定义:

普通属性定义其 setter/getter 方法 如下:

1
2
public <PropertyType> get<PropertyName>();
public void set<PropertyName>(<PropertyType> a);

但布尔类型则做了单独定义,如下:

1
2
public boolean is<PropertyName>();
public void set<PropertyName>(boolean m);

布尔类型的属性使用 Intellij IDEA 自动生成 setter/getter方法时,会遵循如下规律:

  • 基本类型自动生成的 getter 和 setter 方法,名称都是isXXX()setXXX()形式的。
  • 包装类型自动生成的 getter 和 setter 方法,名称都是getXXX()setXXX()形式的。

演示示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
public class ResponseHelper {
public static void main(String[] args) {
Model0 model0 = new Model0();
Model1 model1 = new Model1(true);
Model2 model2 = new Model2(true);
Model21 model21 = new Model21(true);//手动创建的 settter 方法
Model3 model3 = new Model3(true);
Model31 model31 = new Model31(true);//手动创建的 settter 方法
Model4 model4 = new Model4(true);

System.out.println("--------------FastJson序列化---------------------");
System.out.println("无success:" + JSON.toJSONString(model0));
System.out.println("基本类型success:" + JSON.toJSONString(model1));
System.out.println("基本类型isSuccess:" + JSON.toJSONString(model2));
System.out.println("手动isIsSuccess:" + JSON.toJSONString(model21));
System.out.println("包装类型isSuccess:" + JSON.toJSONString(model3));
System.out.println("手动getIsSuccess:" + JSON.toJSONString(model31));
System.out.println("普通succeed:" + JSON.toJSONString(model4));

// 输出结果
// 无success:{"success":true}
// 基本类型success:{"success":true}
// 基本类型isSuccess:{"success":true}
// 手动isIsSuccess:{"isSuccess":true}
// 包装类型isSuccess:{"success":true}
// 手动getIsSuccess:{"isSuccess":true}
// 普通succeed:{"succeed":true}

System.out.println("----------------Gson序列化-----------------------");
Gson gson = new Gson();
System.out.println("无success:" + gson.toJson(model0)); //{"success":true}
System.out.println("基本类型success:" + gson.toJson(model1)); //{"success":true}
System.out.println("基本类型isSuccess:" + gson.toJson(model2)); //{"success":true}
System.out.println("手动isIsSuccess:" + gson.toJson(model21)); //{"isSuccess":true}
System.out.println("包装类型isSuccess:" + gson.toJson(model3)); //{"success":true}
System.out.println("手动getIsSuccess:" + gson.toJson(model31)); //{"isSuccess":true}
System.out.println("普通succeed:" + gson.toJson(model4)); //{"succeed":true}

//无success:{"code":200}
//基本类型success:{"success":true}
//基本类型isSuccess:{"isSuccess":true}
//手动isIsSuccess:{"isSuccess":true}
//包装类型isSuccess:{"isSuccess":true}
//手动getIsSuccess:{"isSuccess":true}
//普通succeed:{"succeed":true}
}
}

class Model0 {
public static final int SUCCESS_CODE = HttpStatus.OK.value();

private int code = SUCCESS_CODE;

// isSuccess 序列化会被解析为 success 属性
public boolean isSuccess() {
return code == SUCCESS_CODE;
}
}

class Model1 {
private boolean success;

public Model1(boolean success) {
this.success = success;
}
// 布尔基本类型, getter 方法以 is 为前缀
public boolean isSuccess() {
return success;
}

public void setSuccess(boolean success) {
this.success = success;
}
}

class Model2 {
private boolean isSuccess;

public Model2(boolean isSuccess) {
this.isSuccess = isSuccess;
}

// 问题来了:以 is 开头的布尔类型属性, IDEA 自动生成的 getter 方法, 会丢失属性原本就有的 is 前缀
// 在使用 FastJson 和 JackSon 序列化时会被解析为 success 属性
// 其正确的 getter 方法应该是 isIsSuccess(){}
public boolean isSuccess() {
return isSuccess;
}

public void setSuccess(boolean success) {
isSuccess = success;
}
}

class Model21 {
private boolean isSuccess;

public Model21(boolean isSuccess) {
this.isSuccess = isSuccess;
}

// 手动设置getter方法, 序列化时解析属性为 isSuccess
public boolean isIsSuccess() {
return isSuccess;
}

public void setIsSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
}

class Model3 {
private Boolean isSuccess;

public Model3(Boolean isSuccess) {
this.isSuccess = isSuccess;
}

// 问题来了:以 is 开头的布尔类型属性, IDEA 自动生成的 getter 方法, 会丢失属性原本就有的 is 前缀
// 在使用 FastJson 和 JackSon 序列化时会被解析为 success 属性
// 其正确的 getter 方法应该是 getIsIsSuccess(){}
public Boolean getSuccess() {
return isSuccess;
}

public void setSuccess(Boolean success) {
isSuccess = success;
}
}

class Model31 {
private Boolean isSuccess;

public Model31(Boolean isSuccess) {
this.isSuccess = isSuccess;
}
// 手动设置getter方法, 序列化时解析属性为 isSuccess
public Boolean getIsSuccess() {
return isSuccess;
}

public void setIsSuccess(Boolean isSuccess) {
this.isSuccess = isSuccess;
}
}

class Model4 {
private Boolean succeed;

public Model4(Boolean succeed) {
this.succeed = succeed;
}

public Boolean getSucceed() {
return succeed;
}

public void setSucceed(Boolean succeed) {
this.succeed = succeed;
}
}

根本原因是 JSON 框架在序列化时对布尔类型的处理,FastJson 和 Jackson 框架序列化是通过反射遍历所有 getter 方法找到属性,就会把 isSuccess() 的方法序列成 success 属性,属性值为该方法的返回值。

此问题在 Gson 中则不存在,Gson 框架序列化是通过反射遍历类中的所有属性,只会序列化存在的属性,就不存在把 isSuccess() 的方法序列成 success 属性的问题。

在阿里巴巴Java开发手册中关于这一点,有过一个『强制性』规定:

POJO不加is

问题解决

方式一:布尔类型变量,不加 is。——建议

方式二:不序列化 isSuccess() 方法,在该方法上添加 @JsonIgnore 注解忽略序列化。——不建议

方式三:手动设置布尔类型的 getter 方法,例如基础类型的 isIsSuccess(),包装类型的 getIsSuccess()。——不建议

相关参考

  1. 为什么强烈禁止开发人员使用isSuccess作为变量名
作者

光星

发布于

2021-10-15

更新于

2023-03-06

许可协议

评论