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);}...
}

如果规避以上的坑呢?

  1. 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;
}
  1. 如果我既想要根据code字段也想要根据Enum.name()来反序列化呢?自定义反序列化方法并注解@JsonParser
@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;}
}
  1. 可以打开反序列化配置objectMapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS); 来避免int匹配不到时根据index来匹配,会直接报错。但并不影响@JsonValue@JsonCreator的使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/456087.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

专业135+总分400+西安交通大学815869(原909)信号与系统考研经验电子信息与通信工程,真题,大纲,参考书

经过将近一年的考研复习&#xff0c;终于梦圆西安交大&#xff0c;今年专业课815(和专硕869&#xff08;原909&#xff09;差不多)信号与系统135&#xff0c;总分400&#xff0c;回想这一年的复习还有很多经验和大家分享&#xff0c;希望可以对大家复习有所帮助&#xff0c;少走…

3.cpp基本数据类型

cpp基本数据类型 1.cpp基本数据类型 1.cpp基本数据类型 C基本数据类型和C语言的基本数据类型差不多 注意bool类型&#xff1a;存储真值 true 或假值 false&#xff0c;C语言编译器C99以上支持。 C语言的bool类型&#xff1a;要添加 #include <stdbool.h>头文件 #includ…

数据库相关知识点

1. 数据库分片与分区 分片&#xff08;Sharding&#xff09;&#xff1a;这是一种将数据水平分割的技术&#xff0c;每个分片包含数据的一个子集。分片通常用于提高数据库的扩展性和性能&#xff0c;特别是在处理大量数据时。通过将数据分布在多个分片上&#xff0c;可以并行处…

ruoyi域名跳转缓存冲突问题(解决办法修改:session名修改session的JSESSIONID名称)

【版权所有&#xff0c;文章允许转载&#xff0c;但须以链接方式注明源地址&#xff0c;否则追究法律责任】【创作不易&#xff0c;点个赞就是对我最大的支持】 前言 仅作为学习笔记&#xff0c;供大家参考 总结的不错的话&#xff0c;记得点赞收藏关注哦&#xff01; 目录 前…

Maven基础知识

一、Maven的概述 maven 是什么&#xff1f; 是一个项目管理工具&#xff0c;它包含了一个项目对象模型&#xff0c;一组标准集合&#xff0c;一个项目的生命周期&#xff0c;一个依赖管理系统&#xff0c;和用来运行定义在生命周期阶段和插件目标的逻辑。 二、Maven的依赖管理…

【331】基于Springboot的“有光”摄影分享网站系统

“有光”摄影分享网站设计与实现 摘 要 自互联网的发展至今&#xff0c;其基础理论与技术都已完善&#xff0c;并积极参与了整个社会各个领域。它容许信息根据媒体传播&#xff0c;并和信息可视化工具一起为大家提供优质的服务。对于信息多头管理、差错率高、信息安全系数差、…

Redis 命令集 (超级详细)

目录 Redis 常用命令集 string类型 hash类型 list类型 set类型 zset类型 bitmap 类型 geo 类型 GEOADD (添加地理位置的坐标) GEOPOS (获取地理位置的坐标) GEODIST (计算两个位置之间的距离) GEOHASH (返回一个或多个位置对象的 geohash 值) GEORADIUS (根据用户…

本地docker部署中间件和应用

Docker Desktop搭建 安装完成之后使用docker下载镜像&#xff0c;报以下错误&#xff1a; 解决办法&#xff1a; Docker Engine配置能访问的镜像地址&#xff1a; {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled…

Vue实现手风琴功能组件 vue 实现折叠面板功能

Vue实现手风琴功能组件 vue 实现折叠面板功能,前端不使用第三方组件实现手风琴折叠面板功能 效果图: dom<div class="foldSection"><divv-for="(item, index) in ListData":key="item.id"class="accordion"><div …

反编译工具jadx

一.官网 https://github.com/skylot/jadx 下载解压即可 二.使用 将想要反编译的apk文件拖入jadx中&#xff0c;等待反编译结束。 三.提醒 反编译出来的只能帮你理解逻辑&#xff0c;并且一般apk都会有加密。

SLAM|2. 差异与统一:坐标系变换与外参标定

本章主要内容 1.坐标系变换 2.相机外参标定 上一章我们了解了相机内参的概念&#xff0c;内参主要解决三维世界与二维图像之间的映射关系。有了内参我们可以一定程度上还原相机看到了什么&#xff08;但缺乏尺度&#xff09;。但相机看到的数据只是处于相机坐标系&#xff0c;为…

2021亚洲机器学习会议:面向单阶段跨域检测的域自适应YOLO(ACML2021)

原文标题&#xff1a;Domain Adaptive YOLO for One-Stage Cross-Domain Detection 中文标题&#xff1a;面向单阶段跨域检测的域自适应YOLO 1、Abstract 域转移是目标检测器在实际应用中推广的主要挑战。两级检测器的域自适应新兴技术有助于解决这个问题。然而&#xff0c;两级…

【Qt6聊天室项目】 主界面功能实现

1. 获取当前用户的个人信息 1.1 前后端逻辑分析&#xff08;主界面功能&#xff09; 主界面上所有的前后端交互逻辑相同&#xff0c;分析到加载会话列表后其余功能仅实现。 核心逻辑总结 异步请求-响应模型 客户端发起请求&#xff0c;向服务器发送包含会话ID的请求服务端处…

ASP.NET Core 8.0 中使用 Hangfire 调度 API

在这篇博文中&#xff0c;我们将引导您完成将 Hangfire 集成到 ASP.NET Core NET Core 项目中以安排 API 每天运行的步骤。Hangfire 是一个功能强大的库&#xff0c;可简化 .NET 应用程序中的后台作业处理&#xff0c;使其成为调度任务的绝佳选择。继续阅读以了解如何设置 Hang…

计算机毕业设计PySpark+大模型高考推荐系统 高考分数线预测 高考爬虫 协同过滤推荐算法 Vue.js Django Hadoop 大数据毕设

基于Spark的高考报名信息推荐系统 系统用到的各项技术和工具的介绍&#xff1a; 1. Python Python是一种高级的、解释型的程序设计语言&#xff0c;因为其简洁而易学、可读性强等特点&#xff0c;在数据处理、人工智能、机器学习、Web开发等领域得到了广泛运用。在该系统中…

php AES 加解密(支持在线运行)

https://andi.cn/page/621792.html 这篇文章不仅给出了 php AES 加解密代码&#xff0c;而且可以在线运行来对数据进行加解密&#xff0c;满足实际中的一些需求。

C语言初阶七:C语言操作符详解(1)

#1024程序员节|征文# 这篇文章是对之前文章中操作符的补充&#xff0c;可以看之前的文章&#xff1a;C语言初阶&#xff1a;六.算数操作_如何用编程表示除法-CSDN博客 C语言操作符是用于执行各种运算和操作的符号。包括算术操作符&#xff08;如、-、*、/、%&#xff09;&#…

Python URL编码

在 Python 中&#xff0c;可以使用 urllib.parse模块对 URL 进行编码。 一、依赖安装 pip install urllib 二、URL编码 from urllib.parse import quoteurl rhttps://myshop.com/shop/shopList?query query {"id":14,"pageSize":10,"pageNum&quo…

[软件工程]—桥接(Brige)模式与伪码推导

桥接&#xff08;Brige&#xff09;模式与伪码推导 1.基本概念 1.1 动机 由于某些类型的固有的实现逻辑&#xff0c;使它们具有两个变化的维度&#xff0c;乃至多个维度的变化。如何应对这种“多维度的变化”&#xff1f;如何利用面向对象技术是的类型可以轻松的沿着两个乃至…

新鲜出炉面试题之【说说spring spring MVC spring boot的区别】

Spring MVC 和 Spring Boot 是 Spring 框架的一部分&#xff0c;但它们的目的和用途有所不同。下面详细阐述这两者之间的区别。 1. 概念 Spring MVC&#xff1a; Spring MVC 是一种基于请求-响应模式的 Web 框架&#xff0c;属于 Spring 框架的一部分。它提供了一种分离的方式…