当前位置: 首页 > 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;美国…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

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

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