Jackson Json序列化反序列化的两个坑
Jackson is a suite of data-processing tools for Java (and the JVM platform)
Jackson最常用的Json序列化功能,引入如下的包即可:
<properties>...<!-- Use the latest version whenever possible. --><jackson.version>2.17.1</jackson.version>...
</properties><dependencies>...<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>${jackson.version}</version></dependency>...
</dependencies>
Json序列化的坑
首先来看一个Json序列化异常。
JsonUtil工具类如下:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;public class JsonUtil {private static final ObjectMapper OBJECT_MAPPER = buildLowerCamelCase();private static ObjectMapper buildLowerCamelCase() {ObjectMapper objectMapper = new ObjectMapper();objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE);return objectMapper;}public static String json(Object obj) {try {return OBJECT_MAPPER.writeValueAsString(obj);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}}
另外有一个实体类CodeMonkey:
import lombok.Data;
import java.util.Objects;@Data
public class CodeMonkey {private Long id;private String name;public boolean isJavaer () {// mock throw exceptionthrow new IllegalArgumentException("should be javaer");}
}
我们对改实体对象进行Json序列化:
public class JacksonTestApplication {public static void main(String[] args) {CodeMonkey codeMonkey = new CodeMonkey();codeMonkey.setId(1L);codeMonkey.setName("not bty");log.info("hello , {}", JsonUtil.json(codeMonkey));}}
运行发现报错:
Exception in thread "main" java.lang.RuntimeException: com.fasterxml.jackson.databind.JsonMappingException: should be javaer (through reference chain: io.github.bty834.jacksontest.CodeMonkey["javaer"])at io.github.bty834.jacksontest.util.JsonUtil.json(JsonUtil.java:22)at io.github.bty834.jacksontest.JacksonTestApplication.main(JacksonTestApplication.java:16)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: should be javaer (through reference chain: io.github.bty834.jacksontest.CodeMonkey["javaer"])at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:392)at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351)...
可以看到Jackson将isJavaer方法当作字段来序列化,但isJavaer其实是不需要序列化的,
可以在方法上添加 com.fasterxml.jackson.annotation.JsonIgnore 注解忽略该方法。
经过调试,Jackson使用com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector#collectAll方法获取某个类型需要序列化的属性信息:
public class POJOPropertiesCollector {...protected void collectAll() {LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();// 最终调用 java.lang.Class#getDeclaredFields 获取字段// final字段和transient字段可配置,默认都序列化_addFields(props); // 最终调用 java.lang.Class#getDeclaredMethods // 获取get/set/is开头的方法(且方法入参分别为0或1,any getter这里不讨论)_addMethods(props);...// 去掉不需要的属性, 如isJavaer方法产生的javaer prop_removeUnwantedProperties(props);...}...
}
Json反序列化的坑
回到CodeMonkey类:
@Data
public class CodeMonkey {private Long id;private String name;private Language language;@JsonIgnorepublic boolean isJavaer () {if (Objects.equals(this.name, "bty")) {return true;}throw new IllegalArgumentException("should be javaer");}
}@Getter
@AllArgsConstructor
public enum Language {JAVA(1, "Java"),C_PLUS_PLUS(2, "C++"),GO(10, "Go");private final int code;private final String name;
}
我们选取两个json字符串模拟反序列化:
@Slf4j
public class JacksonTestApplication {public static void main(String[] args) {// language取1String jsonStr1 = "{\"id\":1,\"name\":\"bty\",\"language\": 1}";CodeMonkey codeMonkey1 = JsonUtil.fromJson(jsonStr1, CodeMonkey.class);System.out.println(codeMonkey1); // CodeMonkey(id=1, name=bty, language=C_PLUS_PLUS)// language取JAVAString jsonStr2 = "{\"id\":1,\"name\":\"bty\",\"language\": \"JAVA\"}";CodeMonkey codeMonkey2 = JsonUtil.fromJson(jsonStr2, CodeMonkey.class);System.out.println(codeMonkey2); // CodeMonkey(id=1, name=bty, language=JAVA)}
}
为啥我们language传1时,反序列化为C_PLUS_PLUS枚举了呢?C_PLUS_PLUS的code字段也不是1啊
在不添加jackson额外注解时,枚举序列化后是枚举值即大写英文字母,而反序列化会经过多个流程。
具体来说,枚举的反序列化默认通过com.fasterxml.jackson.databind.deser.std.EnumDeserializer#deserialize完成:
public class EnumDeserializerextends StdScalarDeserializer<Object>implements ContextualDeserializer
{...@Overridepublic Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException{if (p.hasToken(JsonToken.VALUE_STRING)) {// 1. 如果传的是字符串,则解析字符串return _fromString(p, ctxt, p.getText());}// 如果是int能表示的数字(包含能转为int的long类型)if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) { if (_isFromIntValue) { // 判断根据@JsonValue的值来解析// 将数字转换为字符串根据@JsonValue注解的字段或方法解析return _fromString(p, ctxt, p.getText());}// 根据反序列化配置的CoercionAction取值或报错// CoercionAction 分为 Fail , TryConvert , AsNull , AsEmpty// 枚举这里为TryConvert,具体逻辑参考:com.fasterxml.jackson.databind.cfg.CoercionConfigs#findCoercion// TryConvert会使用枚举值在枚举中的index来匹配这里的int,很坑!return _fromInteger(p, ctxt, p.getIntValue());}// 解析嵌套json对象if (p.isExpectedStartObjectToken()) {return _fromString(p, ctxt,ctxt.extractScalarFromObject(p, this, _valueClass));}// 解析嵌套json数组return _deserializeOther(p, ctxt);}...
}
如果规避以上的坑呢?
@JsonValue
code字段添加@JsonValue,则反序列化只能根据code值(int或int的字符串都行)进行,除了会影响反序列化,序列化时会将该枚举写成其code值
@Getter
@AllArgsConstructor
public enum Language {JAVA(1L, "Java"),C_PLUS_PLUS(2L, "C++"),GO(10L, "Go");@JsonValueprivate final long code;private final String name;
}
@JsonCreator
- 如果我既想要根据
code字段也想要根据Enum.name()来反序列化呢?自定义反序列化方法并注解@JsonCreator
@Getter
@AllArgsConstructor
public enum Language {JAVA(1L, "Java"),C_PLUS_PLUS(2L, "C++"),GO(10L, "Go");@JsonValueprivate final long code;private final String name;@JsonCreatorpublic static Language parse(String val) {for (Language value : Language.values()) {// 先根据Enum.name()判断if (value.name().equals(val)) {return value;}// 再根据code字段判断if (Objects.equals(String.valueOf(value.code), val)) {return value;}}return null;}
}
DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS
可以打开反序列化配置objectMapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS); 来避免int匹配不到时根据index来匹配,会直接报错。但并不影响@JsonValue和@JsonCreator的使用。
相关文章:
Jackson Json序列化反序列化的两个坑
Jackson is a suite of data-processing tools for Java (and the JVM platform) Jackson最常用的Json序列化功能,引入如下的包即可: <properties>...<!-- Use the latest version whenever possible. --><jackson.version>2.17.1<…...
k8s_Pod健康检查
Kubernetes 3种探针介绍 LivenessProbe(存活探针) LivenessProbe 用于检查容器是否仍然活着。如果探针检测到容器已经失去响应,Kubernetes 将重启该容器。这通常用来修复由于内部状态错误或死锁引起的程序失效问题。 作用:检测容器…...
基于DDPG算法的股票量化交易
项目源码获取方式见文章末尾! 回复暗号:13,免费获取600多个深度学习项目资料,快来加入社群一起学习吧。 **《------往期经典推荐------》**项目名称 1.【基于PyQTFaceNet卷积神经网络实现的学生人脸识别考勤系统】 2.【卫星图像道…...
eIQ笔记(UI介绍+Loss曲线+OpenART例程)
This is a very beginner-friendly article ^o^ 目录 🍂一、训练器设置 input size: learning rate: learning rate decay: Epochs: Decay Rate: Linear Decay: Batch Size: Epochs to Train: QAT(Quantization Aware Training)量化感知训练: Pruning剪枝…...
微信小程序——消息订阅
首先用到的就是wx.requestSubscribeMessage接口。 注意:用户发生点击行为或者发起支付回调后,才可以调起订阅消息界面 requestSubscribeMessage() {uni.requestSubscribeMessage({tmplIds: [],//需要订阅的消息模板的id的集合,一次调用最多可…...
网络原理(传输层)->TCP协议解
前言 大家好!我是小帅,今天我们来学习TCP协议,个人主页 文章目录 1. TCP协议2. TCP的核心机制2.1TCP核心机制一:确认应答2.2 TCP核心机制二:超时重传2.3 TCP核心机制三:连接管理2.4 TCP核心机制四…...
oracle imp和exp 导入不同库的用户和表空间
参考: oracle 导入(imp)数据时的表空间(tablespace users)问题_imp tablespace-CSDN博客 网上的解决办法大概都是这种,但是实际测试19c数据库并不能成功,所以最后采取在导出文件上强行修改表空间的办法,改完后再继续执行导出导入…...
滚珠丝杆的精度级别如何分?
滚珠丝杆是一种常见的线性传动装置,广泛应用于各种机械设备和自动化系统中。滚珠丝杆的精度等级划分是评估其传动精度和运动平稳度的重要标准,滚珠丝杆的精度级别划分主要基于传动中实际移动距离与理想移动距离的偏差,偏差越小,精…...
ComfyUI初体验
ComfyUI 我就不过多介绍了,安装和基础使用可以看下面大佬的视频,感觉自己靠图文描述的效果不一定好,大家看视频比较方便。 ComfyUI全球爆红,AI绘画进入“工作流时代”?做最好懂的Comfy UI入门教程:Stable D…...
DPI-C动态库so的使用
文章目录 前言一、方法介绍二、demo演示2.1 文件准备2.2 执行仿真2.3 仿真结果 总结 前言 在做IC验证EDA仿真过程中,有时候需要调用C实现的参考模块,我们可以利用DPI-C的功能,实现SV侧调用C侧的函数。 在具体实现过程中,我们可以…...
Java避坑案例 - 高并发场景下的分布式缓存策略
文章目录 概述缓存常见问题及解决方案把 Redis 当作数据库常用的数据淘汰策略如何选择合适的驱逐算法 缓存雪崩问题复现解决方案 缓存击穿(热点缓存失效)问题复现解决方案 缓存穿透问题复现解决方案缓存穿透 vs 缓存击穿 缓存与数据库的一致性先更新缓存…...
Python中的字符串修剪:strip()、lstrip() 和 rstrip()
Python中的字符串修剪 Python 中的字符串修剪:strip()、lstrip() 和 rstrip()strip()lstrip()rstrip()应用场景结论 Python 中的字符串修剪:strip()、lstrip() 和 rstrip() 在 Python 开发中,我们经常需要处理字符串,其中一项常见…...
K8S配置storage-class
简介 Kubernetes支持NFS存储,需要安装nfs-subdir-external-provisioner,它是一个存储资源自动调配器,它可将现有的NFS服务器通过持久卷声明来支持Kubernetes持久卷的动态分配。该组件是对Kubernetes NFS-Client Provisioner的扩展࿰…...
多线程——线程池
目录 前言 一、什么是线程池 1.引入线程池的原因 2.线程池的介绍 二、标准库中的线程池 1.构造方法 2.方法参数 (1)corePoolSize 与 maximumPoolSize (2)keepAliveTime 与 unit (3)workQueue&am…...
VScode插件:前端每日一题
大文件上传如何做断点续传? 在前端实现大文件上传的断点续传,通常会将文件切片并分块上传,记录每块的上传状态,以便在中断或失败时只上传未完成的部分。以下是实现断点续传的主要步骤和思路: 1. 文件切片 (File Slici…...
Android跨进程通信
1、跨进程通信的几种方式 在 Android 中,跨进程通信 (IPC, Inter-Process Communication) 方式有多种,主要用于在不同的应用或进程之间传递数据。常见的跨进程通信方式包括: AIDL (Android Interface Definition Language) • 描述ÿ…...
【初阶数据结构】计数排序 :感受非比较排序的魅力
文章目录 前言1. 什么是计数排序?2. 计数排序的算法思路2.1 绝对位置和相对位置2.2 根据计数数组的信息来确认 3. 计数排序的代码4. 算法分析5. 计数排序的优缺点6.计数排序的应用场景 前言 如果大家仔细思考的话,可能会发现这么一个问题。我们学的七大…...
前后双差速轮之LQR控制
在之前的代码中,我们实现了前后两对双差速轮AGV的运动学正解和逆解。但为了实现对AGV的精确路径跟踪和姿态控制,我们需要引入控制算法。线性二次型调节器(LQR)是一种常用的最优控制方法,可以有效地将系统的状态误差最小化。本文将详细说明如何在之前的C++代码中加入LQR控制…...
Linux之远程连接服务器
1、远程连接服务器简介 (1)什么是远程连接服务器 远程连接服务器通过文字或图形接口方式来远程登录系统,让你在远程终端前登录linux主机以取得可操作主机接口(shell),而登录后的操作感觉就像是坐在系统前面…...
k8s 部署 nexus3 详解
创建命名空间 nexus3-namespace.yaml apiVersion: v1 kind: Namespace metadata:name: nexus-ns创建pv&pvc nexus3-pv-pvc.yaml apiVersion: v1 kind: PersistentVolume metadata:name: nfs-pvnamespace: nexus-ns spec:capacity:storage: 3GiaccessModes:- ReadWriteM…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
