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

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_PLUScode字段也不是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

  1. 如果我既想要根据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序列化功能&#xff0c;引入如下的包即可&#xff1a; <properties>...<!-- Use the latest version whenever possible. --><jackson.version>2.17.1<…...

k8s_Pod健康检查

Kubernetes 3种探针介绍 LivenessProbe&#xff08;存活探针&#xff09; LivenessProbe 用于检查容器是否仍然活着。如果探针检测到容器已经失去响应&#xff0c;Kubernetes 将重启该容器。这通常用来修复由于内部状态错误或死锁引起的程序失效问题。 作用&#xff1a;检测容器…...

基于DDPG算法的股票量化交易

项目源码获取方式见文章末尾&#xff01; 回复暗号&#xff1a;13&#xff0c;免费获取600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 **《------往期经典推荐------》**项目名称 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接口。 注意&#xff1a;用户发生点击行为或者发起支付回调后&#xff0c;才可以调起订阅消息界面 requestSubscribeMessage() {uni.requestSubscribeMessage({tmplIds: [],//需要订阅的消息模板的id的集合&#xff0c;一次调用最多可…...

网络原理(传输层)->TCP协议解

前言 大家好&#xff01;我是小帅&#xff0c;今天我们来学习TCP协议&#xff0c;个人主页 文章目录 1. TCP协议2. TCP的核心机制2.1TCP核心机制一&#xff1a;确认应答2.2 TCP核心机制二&#xff1a;超时重传2.3 TCP核心机制三&#xff1a;连接管理2.4 TCP核心机制四&#xf…...

oracle imp和exp 导入不同库的用户和表空间

参考&#xff1a; oracle 导入(imp)数据时的表空间(tablespace users)问题_imp tablespace-CSDN博客 网上的解决办法大概都是这种&#xff0c;但是实际测试19c数据库并不能成功&#xff0c;所以最后采取在导出文件上强行修改表空间的办法&#xff0c;改完后再继续执行导出导入…...

滚珠丝杆的精度级别如何分?

滚珠丝杆是一种常见的线性传动装置&#xff0c;广泛应用于各种机械设备和自动化系统中。滚珠丝杆的精度等级划分是评估其传动精度和运动平稳度的重要标准&#xff0c;滚珠丝杆的精度级别划分主要基于传动中实际移动距离与理想移动距离的偏差&#xff0c;偏差越小&#xff0c;精…...

ComfyUI初体验

ComfyUI 我就不过多介绍了&#xff0c;安装和基础使用可以看下面大佬的视频&#xff0c;感觉自己靠图文描述的效果不一定好&#xff0c;大家看视频比较方便。 ComfyUI全球爆红&#xff0c;AI绘画进入“工作流时代”&#xff1f;做最好懂的Comfy UI入门教程&#xff1a;Stable D…...

DPI-C动态库so的使用

文章目录 前言一、方法介绍二、demo演示2.1 文件准备2.2 执行仿真2.3 仿真结果 总结 前言 在做IC验证EDA仿真过程中&#xff0c;有时候需要调用C实现的参考模块&#xff0c;我们可以利用DPI-C的功能&#xff0c;实现SV侧调用C侧的函数。 在具体实现过程中&#xff0c;我们可以…...

Java避坑案例 - 高并发场景下的分布式缓存策略

文章目录 概述缓存常见问题及解决方案把 Redis 当作数据库常用的数据淘汰策略如何选择合适的驱逐算法 缓存雪崩问题复现解决方案 缓存击穿&#xff08;热点缓存失效&#xff09;问题复现解决方案 缓存穿透问题复现解决方案缓存穿透 vs 缓存击穿 缓存与数据库的一致性先更新缓存…...

Python中的字符串修剪:strip()、lstrip() 和 rstrip()

Python中的字符串修剪 Python 中的字符串修剪&#xff1a;strip()、lstrip() 和 rstrip()strip()lstrip()rstrip()应用场景结论 Python 中的字符串修剪&#xff1a;strip()、lstrip() 和 rstrip() 在 Python 开发中&#xff0c;我们经常需要处理字符串&#xff0c;其中一项常见…...

K8S配置storage-class

简介 Kubernetes支持NFS存储&#xff0c;需要安装nfs-subdir-external-provisioner&#xff0c;它是一个存储资源自动调配器&#xff0c;它可将现有的NFS服务器通过持久卷声明来支持Kubernetes持久卷的动态分配。该组件是对Kubernetes NFS-Client Provisioner的扩展&#xff0…...

多线程——线程池

目录 前言 一、什么是线程池 1.引入线程池的原因 2.线程池的介绍 二、标准库中的线程池 1.构造方法 2.方法参数 &#xff08;1&#xff09;corePoolSize 与 maximumPoolSize &#xff08;2&#xff09;keepAliveTime 与 unit &#xff08;3&#xff09;workQueue&am…...

VScode插件:前端每日一题

大文件上传如何做断点续传&#xff1f; 在前端实现大文件上传的断点续传&#xff0c;通常会将文件切片并分块上传&#xff0c;记录每块的上传状态&#xff0c;以便在中断或失败时只上传未完成的部分。以下是实现断点续传的主要步骤和思路&#xff1a; 1. 文件切片 (File Slici…...

Android跨进程通信

1、跨进程通信的几种方式 在 Android 中&#xff0c;跨进程通信 (IPC, Inter-Process Communication) 方式有多种&#xff0c;主要用于在不同的应用或进程之间传递数据。常见的跨进程通信方式包括&#xff1a; AIDL (Android Interface Definition Language) • 描述&#xff…...

【初阶数据结构】计数排序 :感受非比较排序的魅力

文章目录 前言1. 什么是计数排序&#xff1f;2. 计数排序的算法思路2.1 绝对位置和相对位置2.2 根据计数数组的信息来确认 3. 计数排序的代码4. 算法分析5. 计数排序的优缺点6.计数排序的应用场景 前言 如果大家仔细思考的话&#xff0c;可能会发现这么一个问题。我们学的七大…...

前后双差速轮之LQR控制

在之前的代码中,我们实现了前后两对双差速轮AGV的运动学正解和逆解。但为了实现对AGV的精确路径跟踪和姿态控制,我们需要引入控制算法。线性二次型调节器(LQR)是一种常用的最优控制方法,可以有效地将系统的状态误差最小化。本文将详细说明如何在之前的C++代码中加入LQR控制…...

Linux之远程连接服务器

1、远程连接服务器简介 &#xff08;1&#xff09;什么是远程连接服务器 远程连接服务器通过文字或图形接口方式来远程登录系统&#xff0c;让你在远程终端前登录linux主机以取得可操作主机接口&#xff08;shell&#xff09;&#xff0c;而登录后的操作感觉就像是坐在系统前面…...

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…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...

前端工具库lodash与lodash-es区别详解

lodash 和 lodash-es 是同一工具库的两个不同版本&#xff0c;核心功能完全一致&#xff0c;主要区别在于模块化格式和优化方式&#xff0c;适合不同的开发环境。以下是详细对比&#xff1a; 1. 模块化格式 lodash 使用 CommonJS 模块格式&#xff08;require/module.exports&a…...

【工具教程】多个条形码识别用条码内容对图片重命名,批量PDF条形码识别后用条码内容批量改名,使用教程及注意事项

一、条形码识别改名使用教程 打开软件并选择处理模式&#xff1a;打开软件后&#xff0c;根据要处理的文件类型&#xff0c;选择 “图片识别模式” 或 “PDF 识别模式”。如果是处理包含条形码的 PDF 文件&#xff0c;就选择 “PDF 识别模式”&#xff1b;若是处理图片文件&…...

DL00871-基于深度学习YOLOv11的盲人障碍物目标检测含完整数据集

基于深度学习YOLOv11的盲人障碍物目标检测&#xff1a;开启盲人出行新纪元 在全球范围内&#xff0c;盲人及视觉障碍者的出行问题一直是社会关注的重点。尽管技术不断进步&#xff0c;许多城市的无障碍设施依然未能满足盲人出行的实际需求。尤其是在复杂的城市环境中&#xff…...