记一次JSON.toJSONString()转换时非属性方法空指针异常排查及toJSONString保留null值属性
异常详情
有一个类,里面有两个属性和一个类似工具的getRealName()方法如下:
getRealName()方法就是获取这个人的真实名字,如果获取不到就以name返回
class JSONTest {String name;String age;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public String getRealName() {try {return "real" + this.name.substring(0, 1);} catch (Exception e) {e.printStackTrace();}return this.name;}}
然后Controller中有两个url使用了这个类,并转成JSONString返回。下面就以两个方法模仿这两个url。
public class JsonMainTest {public static void main(String[] args) throws Exception {System.out.println(method01());System.out.println(method02());}static String method01() {JSONTest jsonTest = new JSONTest();jsonTest.setName("lowkey");jsonTest.setAge("18");jsonTest.setName(jsonTest.getRealName());return JSON.toJSONString(jsonTest);}static String method02() {JSONTest jsonTest = new JSONTest();jsonTest.setAge("18");return JSON.toJSONString(jsonTest);}
}
接下来运行这个main方法,可以看到第二个方法报错了
异常原因
一开始很好奇为什么会异常,因为空指针,name为空了,但是我method02方法并没有调用getRealName(),为什么会调用了它,并空指针了呢。
后面细究发现是因为JSON.toJSONString()的时候是根据getter方法进行的,也就是下面这行
它会把所有符合以get开头的方法拿出来然后把它转成属性进行设置,所有他会在转method02方法的时候调用了getRealName()方法,而method02方法中name并没有设置值,所有才出现了空指针异常。在过程中还发现它会扫描以is开头的方法。
从打印的JSONString串也可以看出,我的属性里面并没有realName属性它却打印了出来。
解决方案
-
第一种
就是规范命名,与类属性无关的方法不要以get/is开头,向我这个类里面getRealName只是将name进行了处理,并不是作为一个类属性使用,所以我们将该方法改成handleRealName()或者其他即可。如下所示:再次运行便正常了,打印中也没有除属性外的字段。
public class JsonMainTest {public static void main(String[] args) throws Exception {System.out.println(method01());System.out.println(method02());}static String method01() {JSONTest jsonTest = new JSONTest();jsonTest.setName("lowkey");jsonTest.setAge("18");jsonTest.setName(jsonTest.handletRealName());return JSON.toJSONString(jsonTest);}static String method02() {JSONTest jsonTest = new JSONTest();jsonTest.setAge("18");return JSON.toJSONString(jsonTest);} }class JSONTest {String name;String age;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public String handletRealName() {try {return "real" + this.name.substring(0, 1);} catch (Exception e) {e.printStackTrace();}return this.name;} }
-
第二种
忽略JSON转换,即在进行JSON.toJSONString的时候忽略getRealName方法。在方法上添加@JSONField(serialize = false)注解,如下所示:
public class JsonMainTest {public static void main(String[] args) throws Exception {System.out.println(method01());System.out.println(method02());}static String method01() {JSONTest jsonTest = new JSONTest();jsonTest.setName("lowkey");jsonTest.setAge("18");jsonTest.setName(jsonTest.getRealName());return JSON.toJSONString(jsonTest);}static String method02() {JSONTest jsonTest = new JSONTest();jsonTest.setAge("18");return JSON.toJSONString(jsonTest);} }class JSONTest {String name;String age;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}@JSONField(serialize = false)public String getRealName() {try {return "real" + this.name.substring(0, 1);} catch (Exception e) {e.printStackTrace();}return this.name;} }
-
不过建议还是规范命名
其他问题:空属性不打印的情况
我们还会发现为空的属性,toJSONStirng的时候不打印。我们可以使用时添加SerializerFeature.WriteMapNullValue属性:
JSON.toJSONString(jsonTest, SerializerFeature.WriteMapNullValue);