需求开发的过程中,经常会用JSON序列化。通过JSON序列化将Java对象转换为JSON格式的数据,以便在不同的平台和编程语言之间进行数据交换。某次在排查空指针报错的时候,最后竟发现是由于使用了JSON.toJSONString()进行序列化而导致的。下面用简单的代码去复现遇到的问题。
package com.example.demo.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Man {
private String name;
private int age;
// 根据对象中的name解析出realName
public String getRealName(){
return "real" + this.name.charAt(0);
}
}
测试代码如下:
public static void main(String[] args) {
Man man = new Man();
man.setAge(15);
String s = JSON.toJSONString(man);
System.out.println(s);
}
错误的堆栈信息如下:
根据错误信息可以定位到是
Man.getRealName的第19行,这一行return "real" + this.name.charAt(0),能报npe的唯一可能就是this.name是null,this.name确实是null,但是代码中看上去似乎并没有主动的去调用过Man.getRealName这个方法,那就是被隐式调用了。为了验证猜想,在Man.getRealName方法入口处打印一行日志:
public String getRealName(){
System.out.println("=========");
return "real" + this.name.charAt(0);
}
可以看到Man.getRealName这个方法确实被调用了,因为测试代码比较简单,所以能够很快的定位到就是JSON.toJSONString()序列化man对象的时候,内部调用了Man.getRealName这个方法,然后因为man对象中并未设置name属性,所以导致了npe
带着这个问题,网上查阅了资料,得到以下结论:
- JSON.toJSONString()序列化对象的时候是根据对象中的get方法/is方法实现的,它会去依次获取到所有符合以get/is开头的方法,然后把它转成对象属性,尽管对象中并不存在这个属性
针对这个情况,给出以下解决方案:
- 规范方法命名。与类属性无关的方法不要以get/is开头,可以使用fetch/judge/hanle等作为方法开头
- 使用注解忽略某些字段的序列化。在方法上添加@JSONField(serialize = false)注解即可