当前位置: 首页 > news >正文

fastjson解析自定义get方法导致空指针问题

背景

为了在日志中把出入参打印出来,以便验证链路和排查问题,在日志中将入参用fastjson格式化成字符串输出,结果遇到了NPE。
在这里插入图片描述

问题复现

示例代码

public static void main(String[] args) {OrganizationId orgId = new OrganizationId();NodeName name = new NodeName("test");Node node = new Node();node.setName(name);node.setOrganizationId(orgId);System.out.println(JSONObject.toJSONString(node));
}

错误提示
在这里插入图片描述
发现是OrganizationId对象里的方法报空指针了,赶紧看一眼这个类:

public class OrganizationId {private String id;public Long getIdToLong() {return Long.valueOf(this.id);}
}

怎么会运行到 getIdToLong 方法呢?

问题排查

对 JSONObject.toJSONString 方法进行反复 debug 之后,终于发现了原因,以下是具体路径:

public static String toJSONString(Object object, SerializeConfig config, SerializeFilter[] filters, String dateFormat,int defaultFeatures, SerializerFeature... features) {SerializeWriter out = new SerializeWriter(null, defaultFeatures, features);try {JSONSerializer serializer = new JSONSerializer(out, config);if (dateFormat != null && dateFormat.length() != 0) {serializer.setDateFormat(dateFormat);serializer.config(SerializerFeature.WriteDateUseDateFormat, true);}if (filters != null) {for (SerializeFilter filter : filters) {serializer.addFilter(filter);}}serializer.write(object);return out.toString();} finally {out.close();}
}

往下到 serializer.write 方法:

 public final void write(Object object) {if (object == null) {out.writeNull();return;}Class<?> clazz = object.getClass();ObjectSerializer writer = getObjectWriter(clazz);try {writer.write(this, object, null, null, 0);} catch (IOException e) {throw new JSONException(e.getMessage(), e);}}

在这里插入图片描述
再到 getObjectWriter,注意入参create传了true:

public ObjectSerializer getObjectWriter(Class<?> clazz) {return getObjectWriter(clazz, true);
}

在 getObjectWriter 的核心具体实现中,走到了自定义对象序列化的流程:

// ......
if (create) {writer = createJavaBeanSerializer(clazz);put(clazz, writer);
}

createJavaBeanSerializer 往下到 TypeUtils.buildBeanInfo:

public final ObjectSerializer createJavaBeanSerializer(Class<?> clazz) {SerializeBeanInfo beanInfo = TypeUtils.buildBeanInfo(clazz, null, propertyNamingStrategy, fieldBased);if (beanInfo.fields.length == 0 && Iterable.class.isAssignableFrom(clazz)) {return MiscCodec.instance;}return createJavaBeanSerializer(beanInfo);
}

在这里插入图片描述

在 buildBeanInfo 中,由于入参 fieldBased 是false,会走到 computeGetters 的逻辑:

List<FieldInfo> fieldInfoList = fieldBased? computeGettersWithFieldBase(beanType, aliasMap, false, propertyNamingStrategy) //: computeGetters(beanType, jsonType, aliasMap, fieldCacheMap, false, propertyNamingStrategy);

在这里插入图片描述

看到 computeGetters 的名字,感觉八成是这里了,发现里面有一段逻辑是扫描以 get 开头的方法名,把方法后缀变成一个属性,后续在获取对应属性时,会去运行对应的 getter 方法:

if(methodName.startsWith("get")){// 省略...// 从方法名中解析出属性名propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
}

在这里插入图片描述
在这里插入图片描述

从上面这段代码可以获取到 propertyName 的值为 idToLong,并且对应的 fieldInfo 是 getIdToLong 方法。
到这里基本水落石出了,原来是fastjson序列化是扫描以 “get”(还有“is”) 开头的方法,并且从该方法名中提取属性,如果对应的方法中存在问题,那么这里就可能遇到对应的异常,就像本文遇到的NPE。

解决方案

1、 业务逻辑中处理:保证 node 对象中的 orgId 不为空,避免NPE。
2、日志打印中处理:不序列化整个对象,只打出关键信息,避开可能为空的字段。
3、 在调用JSON.toJSONString的时候,加上SerializerFeature.IgnoreNonFieldGetter参数,忽略掉所有没有对应成员变量(Field)的getter函数,可以正常序列化。

JSONObject.toJSONString(node, SerializerFeature.IgnoreNonFieldGetter)

4、 通过在函数上 getXxx() 增加@JSONField(serialize = false)注解,也能达到同样的效果。

@JSONField(serialize = false)
public Long getIdToLong() {return Long.valueOf(this.id);
}

computeGetters 中消费注解的代码:

JSONField annotation = method.getAnnotation(JSONField.class);// ...if(annotation != null){if(!annotation.serialize()){continue;}// ...if(methodName.startsWith("get")){
// ... 

总结

fastjson 将对象转为 string 时,会把以“get”开头的方法认为是属性的 getter,把 getXXX 方法后面的 XXX 变成一个属性,并通过 getXXX 方法去获取,如果get方法内存在异常逻辑,就可能报错。可以尽量避免使用JSON打日志。

附录

1、阿里巴巴开发规约
在这里插入图片描述

2、默认根据get方法进行序列化,根据java bean的定义,通过反射来获取,javaBean定义见:什么是JavaBean、bean?

相关文章:

fastjson解析自定义get方法导致空指针问题

背景 为了在日志中把出入参打印出来&#xff0c;以便验证链路和排查问题&#xff0c;在日志中将入参用fastjson格式化成字符串输出&#xff0c;结果遇到了NPE。 问题复现 示例代码 public static void main(String[] args) {OrganizationId orgId new OrganizationId();N…...

github新手用法详解

GitHub是一个非常强大的版本控制工具&#xff0c;它为程序员提供了一个便捷的方式来管理代码、协作开发和参与开源项目。但对于新手来说&#xff0c;可能会觉得GitHub的使用有些复杂。因此&#xff0c;本篇文章将详细介绍GitHub的基本用法&#xff0c;帮助新手快速上手并充分利…...

MAC电脑系统清理空间免费版软件CleanMyMac X2024

大家好&#xff0c;我是那个总是被苹果电脑“内存已满”提示搞得焦头烂额的专业博主。如果你也像我一样&#xff0c;在使用Mac时经常遭遇卡顿、慢吞吞的情况&#xff0c;那么今天的Mac清理空间妙招分享绝对适合你&#xff01; CleanMyMac X全新版下载如下: https://wm.makedi…...

notepad++运行python闪一下就没啦

问题&#xff1a;Notepad直接快捷键运行Python代码,出现闪一下就没了 解决措施&#xff1a; ①点击菜单运行(Run) --> 运行(Run)弹出的对话框 ②把 cmd /k python "$(FULL_CURRENT_PATH)" & ECHO. & PAUSE & EXIT 粘贴进入这个对话框内 ③点击保存&a…...

基于springboot+vue的课程答疑系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…...

【工具类】非 sudo 运行 docker

非 root 运行 docker 命令 sudo groupadd docker sudo usermod -aG docker $USER newgrp docker sudo chown root:docker /var/run/docker.sock sudo chown "$USER":"$USER" /home/"$USER"/.docker -R sudo chmod grwx "$HOME/.docker&quo…...

力扣49.字母异位词分组

题目描述&#xff1a; 49. 字母异位词分组 难度 中等 给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 示例 1: 输入: strs ["eat", "tea"…...

git操作--->在远程删除了某个分支,但本地使用git branch -r的时候还是会显示某个分支存在是什么原因

&#x1f495;又迷糊了哈哈&#xff0c;以为自己命令执行错了&#xff0c;结果可能是缓存的原因&#xff1a;&#x1f495; &#x1f602;如果你发现使用 git branch -r 命令显示了一个远程没有的分支&#xff0c;这可能是由以下几个原因造成的&#xff1a;&#x1f602; 缓存…...

合并Windows电脑的不同分区(不同的盘)的方法

本文介绍在Windows操作系统的电脑中&#xff0c;将磁盘上的不同分区&#xff08;例如E盘与F盘&#xff09;加以合并的方法。 最近&#xff0c;想着将新电脑的2个分区加以合并&#xff1b;如下图所示&#xff0c;希望将E盘与F盘合并为一个分区。本文就介绍一下实现这一需求的具体…...

web前端安全性——iframe安全问题

1、概念 iframe安全问题可称作界面劫持&#xff0c;像点击劫持、拖放劫持、触屏劫持。就是我们的点击&#xff0c;拖放&#xff0c;触屏操作被劫持了&#xff0c;而去操作了其它的透明隐藏的界面。 **原理是利用透明层iframe,使用了CSS中的opacity或z-index等属性&#xff0c;…...

从零开始学习Netty - 学习笔记 - NIO基础 - 网络编程: Selector

4.网络编程 4.1.非阻塞 VS 阻塞 在网络编程中&#xff0c;**阻塞&#xff08;Blocking&#xff09;和非阻塞&#xff08;Non-blocking&#xff09;**是两种不同的编程模型&#xff0c;描述了程序在进行网络通信时的行为方式。 阻塞&#xff08;Blocking&#xff09;&#xff1…...

useRef有什么用?

看一下官网定义 useRef是一个React Hook&#xff0c;它能帮助引用一个不需要渲染的值 这句话透露出一个信息&#xff0c;不需要渲染的值可以用useRef引用&#xff0c;那需要渲染的值用什么引用呢&#xff1f;当然是useState了&#xff0c;需要渲染的值指的就是状态嘛&#xff0…...

vue3中,ref()、reactive()、computed()、watch() 和 watchEffect()的区别

ref()、reactive()、computed()、watch() 和 watchEffect() 是 Vue 3 中常用的响应式处理函数&#xff0c;它们的主要区别如下&#xff1a; ref()&#xff1a;ref() 函数用于将一个普通的 JavaScript 值转化为响应式对象。它返回一个具有 value 属性的对象&#xff0c;我们可以…...

Java基于SpringBoot的校园轻博客系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…...

webstorm光标变成方块解决办法_webstorm光标变粗不能换行

webstorms光标变了 键盘上的insert是切换的快捷键&#xff0c;敲insert就可以来回切换了...

从计网的角度讲明白什么是网关

网关(Gateway)又称网间连接器、协议转换器。网关在传输层上以实现网络互连&#xff0c;是最复杂的网络互连设备&#xff0c;仅用于两个高层协议不同的网络互连。网关的结构也和路由器类似&#xff0c;不同的是互连层。网关既可以用于广域网互连&#xff0c;也可以用于局域网互连…...

如何选择最适合的图纸加密软件?安秉网盾软件用户体验及性价比

安秉网盾图纸加密软件是一款功能强大的图纸加密工具&#xff0c;具有以下特点和优势&#xff1a; 全盘加密&#xff1a;安秉网盾采用先进的加密算法&#xff0c;能对文件、文件夹、磁盘等数据进行全面加密&#xff0c;确保数据在存储和传输过程中的安全性。 监控与审计&#x…...

Spring Security学习(六)——配置多个Provider(存在两种认证规则)

前言 《Spring Security学习&#xff08;五&#xff09;——账号密码的存取》一文已经能满足一般应用的情况。但实际商业应用也会存在如下的情况&#xff1a;用户提交的账号密码&#xff0c;能在本地的保存的账号密码匹配上&#xff0c;或者能在远端服务认证中匹配上&#xff…...

Js如何判断两个数组是否相等?

本文目录 1、通过数组自带方法比较2、通过循环判断3、toString()4、join()5、JSON.stringify() 日常开发&#xff0c;时不时会遇到需要判定2个数组是否相等的情况&#xff0c;需要实现考虑的场景有&#xff1a; 先判断长度&#xff0c;长度不等必然不等元素位置其他情况考虑 1…...

Salesforce顾问如何拿到更高的薪水?

顾问的角色已经在Salesforce生态系统存在了一段时间&#xff0c;随着Salesforce针对职业发展的Trailhead培训模块的发布&#xff0c;该角色的热度又达到了新的浪潮。越来越多人走上了Salesforce顾问这条职业道路。 当然其薪资水平也非常可观&#xff0c;据调查&#xff0c;美国…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...