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…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
