需求示例
在一个Controller接口中,当Post一个请求过来,请求对象有idCardEnd、sex 两个参数。当反序列化为Java对象时如果idCardEnd为X时,表示性别一定为男,所以直接初始化。所以有了如下写法
@NoArgsConstructor
@AllArgsConstructor
public class Person {
String idCardEnd;
String sex;
public String getIdCardEnd() {
return idCardEnd;
}
public void setIdCardEnd(String idCardEnd) {
this.idCardEnd = idCardEnd;
this.sex = "X".equals(idCardEnd) ? "男": "女";
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
问题
- 那么不传sex的值的时候是会根据idCardEnd去进行判断,那么两个参数同时传,结果会如何呢?
如果是只传一个idCardEnd,那么得到的结果是sex会根据三元表达式的判断为男{ "idCardEnd":"X" }
如果传了两个值,那个会使用sex传的值呢?还是会根据三元表达式进行判断呢?这里传一个idCardEnd=X sex = 女 试试,入参如下:Person(idCardEnd=X, sex=男)
得到的结果如下:{ "idCardEnd":"X", "sex":"女" }
那么为什么会这样呢?是否跟set方法的顺序有关呢?Person(idCardEnd=X, sex=女)
我们试试将set方法的顺序换一下看
此时我们再传一个idCardEnd=X sex = 女试试看,得到的结果如下:@ToString @NoArgsConstructor @AllArgsConstructor public class Person { String idCardEnd; String sex; public String getIdCardEnd() { return idCardEnd; } public void setSex(String sex) { this.sex = sex; } public void setIdCardEnd(String idCardEnd) { this.idCardEnd = idCardEnd; this.sex = "X".equals(idCardEnd) ? "男": "女"; } public String getSex() { return sex; } }
得到的还是一样的结果,看来和Set方法的顺序是没有关系的。那么是否和传参顺序有关呢?为了控制变量,我们先将Set方法的顺序还原,更改参数顺序试试。Person(idCardEnd=X, sex=女)
得到的结果如下:{ "sex":"女", "idCardEnd":"X" }
哎,这下还真和传参顺序有关,是按照传参顺序进行反序列化的。Person(idCardEnd=X, sex=男)
原理分析
在一般情况下,JSON参数中的key值顺序不应该影响映射为Java对象的结果。JSON是一种键值对的无序集合,它不关心元素的排列顺序。因此,JSON中键值对的顺序理论上不应该影响到Java对象的反序列化过程。
Jackson框架通常会使用哈希表或类似的数据结构来存储JSON键值对,这些数据结构不保留元素的插入顺序。因此,在进行反序列化时,Jackson不会关心JSON中键值对的顺序。
但是,要注意的是,在某些特殊情况下,JSON中键值对的顺序可能会影响反序列化结果,这取决于具体的情况和使用的Jackson配置。例如,如果在Java对象中使用了@JsonPropertyOrder注解或者PropertyOrder类级别的注解来指定属性的顺序,那么反序列化时可能会受到这些顺序的影响。
这里我们并未使用任何干扰顺序的注解,那么是为什么呢?我们来看一下这段代码。
public void setIdCardEnd(String idCardEnd) {
this.idCardEnd = idCardEnd;
this.sex = "X".equals(idCardEnd) ? "男" : "女";
}
在这个方法中,setIdCardEnd方法被调用时,会将传入的idCardEnd参数值赋给this.idCardEnd属性,并且根据传入的idCardEnd参数值设置sex属性的值。
如果传参为{“sex”:“女”, “idCardEnd”:“X”},则设置sex为女,设置idCardEnd为X,然后根据条件判断设置sex为男,覆盖原来的sex为女,所以此时sex属性被设置为男。
但如果传参为{“idCardEnd”:“X”, “sex”:“女”},则设置idCardEnd为X,然后根据条件判断设置sex为男,但由于传入的sex已经为女,所以sex属性不会被重新赋值,仍然保持为女。
因此,传参顺序不同导致结果不一致的原因是,setIdCardEnd方法中的设置sex属性值的逻辑是基于传入的idCardEnd参数值进行的,而不是基于传入的sex参数值。