JSON序列化引起的npe

167 阅读2分钟

需求开发的过程中,经常会用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);
}

错误的堆栈信息如下:

image.png 根据错误信息可以定位到是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 image.png

带着这个问题,网上查阅了资料,得到以下结论:

  • JSON.toJSONString()序列化对象的时候是根据对象中的get方法/is方法实现的,它会去依次获取到所有符合以get/is开头的方法,然后把它转成对象属性,尽管对象中并不存在这个属性

针对这个情况,给出以下解决方案:

  1. 规范方法命名。与类属性无关的方法不要以get/is开头,可以使用fetch/judge/hanle等作为方法开头
  2. 使用注解忽略某些字段的序列化。在方法上添加@JSONField(serialize = false)注解即可