【工作中问题解决实践 七】SpringBoot集成Jackson进行对象序列化和反序列化
去年10月份以来由于公司和家里的事情太多,所以一直没有学习,最近缓过来了,学习的脚步不能停滞啊。回归正题,其实前年在学习springMvc的时候也学习过Jackson【Spring MVC学习笔记 五】SpringMVC框架整合Jackson工具,但是呢只是局限于基本用法,当时也刚进入新项目工作没多久,体会也没有那么深刻。现如今工作中深度用到了Jackson,但是对于Jackson的详细情况心里却没有十分的底,大多数时候都是用到的时候从网上找相关的方法实现copy一份,没有全局的认识,所以这篇博客详细的学习和实践一下Jackson。当然市面上的序列化框架有很多,例如谷歌的Gson,阿里的FastJson,但是因为综合考虑性能(Jackson比较强)、稳定性(Jackson和Gson都比较强),再加上SpringBoot默认集成的就是Jackson,所以对于Jackson掌握清楚后就足以应对大多数工作场景了。
回顾Json格式规则
从结构上看,所有的Json格式数据最终都可以分成三种类型:
- 第一种类型是scalar(标量),也就是一个单独的string(字符串)或数字(numbers),比如
"北京"
这个单独的词 - 第二种类型是sequence(序列),也就是若干个相关的数据按照一定顺序并列在一起,又叫做array(数组)或List(列表),比如
["北京","天津"]
- 第三种类型是mapping(映射),也就是一个名/值对(Name/value),即数据有一个名称,还有一个与之相对应的值,这又称作hash(散列)或dictionary(字典),比如
{"城市名称":"北京"}
Json格式规则有如下几种:
- 并列的数据之间用逗号
,
分隔 - 映射用冒号
:
表示 - 并列数据的集合(数组)用方括号
[]
表示 - 映射的集合(对象)用大括号
{}
表示
大多时候我们会用到对象和Json的序列化与反序列化操作。
SpringBoot集成Jackson
当然第一步就是在Maven中进行Jackson包的引入了,还是从Maven的中央仓库引入最新版本的Jackson:Jackson的Maven仓库地址,我们就使用截止目前Jackson更新的最新版本:
pom文件如下:
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.14.2</version>
</dependency>
Jackson核心包概览
Jackson包含三部分的核心包:jackson-core、jackson-annotations、jackson-databind
:
- jackson-core,核心包,提供基于流模式解析的相关 API,它包括
JsonPaser
和JsonGenerator
。 Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。 - jackson-annotations,注解包,提供标准注解功能;
- jackson-databind ,数据绑定包, 提供基于对象绑定解析的相关 API (
ObjectMapper
) 和树模型 解析的相关 API (JsonNode
);基于"对象绑定" 解析的 API 和"树模型"解析的 API 依赖基于"流模式"解析的 API。
当然因为jackson-databind
依赖jackson-core
和jackson-annotations
,所以当添加 jackson-databind 之后, jackson-core 和 jackson-annotations 也随之添加到 Java 项目工程中。
由于我们大多数场景都是处理对象和Json之间的映射关系,所以我们把重点放到对象绑定上来。
核心对象ObjectMapper
大多数的ObjectMapper对象都会被配置到Feature对象。
这篇文章ObjectMapper的一些配置提到的一些默认配置在最新的jackson版本里已经更新了,例如序列化时默认的时间戳格式被取消,序列化时默认的空对象异常也不会抛出了
Jackson基本用法实践
关于Jackson的基本用法如下,直接上代码清单
目标Person对象
我们用来测试的对象
package com.example.springboot.jackson;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDate;
import java.util.List;/*** The type Person.** @author tianmaolin004* @date 2023 /3/18*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Person {private String name;private Integer age;private List<String> interests;private LocalDate birthday;
}
JsonUtils类
包括ObjectMapper对象的初始化以及相关的一些配置,还有转换方法
package com.example.springboot.jackson;import com.alibaba.druid.util.StringUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;/*** The type Json operator.** @author tianmaolin004* @date 2023 /3/18*/
@Slf4j
public class JsonUtils {private static final ObjectMapper objectMapper = new ObjectMapper();private static final String LOCAL_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";static {// 1 序列化及反序列化的时间配置JavaTimeModule timeModule = new JavaTimeModule();timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE));timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE));timeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME));timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME));timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(LOCAL_DATE_TIME_PATTERN)));timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(LOCAL_DATE_TIME_PATTERN)));objectMapper.registerModule(timeModule);objectMapper.setDateFormat(new SimpleDateFormat(LOCAL_DATE_TIME_PATTERN));//2 忽略反序列化时,对象不存在对应属性的错误,如果不存在该属性,则设置值为nullobjectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);//3 忽略序列化时值为Null元素,不存在该元素,则字符串中无该元素,而不是展示为nullobjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);}/*** 对象转字符串** @param <T> the type parameter* @param obj the obj* @return the string*/public static <T> String obj2Str(T obj) {if (obj == null) {return null;}try {return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);} catch (Exception e) {log.error("obj2Str fail");return null;}}/*** 字符串转对象** @param <T> the type parameter* @param str the str* @param clazz the clazz* @return the t*/public static <T> T str2Obj(String str, Class<T> clazz) {if (StringUtils.isEmpty(str) || clazz == null) {return null;}try {return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);} catch (Exception e) {log.error("str2Obj fail");return null;}}/*** 字符串转对象:泛型模式,一般用于集合** @param <T> the type parameter* @param str the str* @param typeReference the type reference* @return the t*/public static <T> T str2Obj(String str, TypeReference<T> typeReference) {if (StringUtils.isEmpty(str) || typeReference == null) {return null;}try {return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));} catch (Exception e) {log.error("str2Obj fail");return null;}}/*** 字符串转JsonNode** @param str the str* @return the json node*/public static JsonNode str2JsonNode(String str) {if (StringUtils.isEmpty(str)) {return null;}try {return objectMapper.readTree(str);} catch (Exception e) {log.error("str2Obj fail");return null;}}/*** 对象互转** @param <T> the type parameter* @param fromValue the from value* @param toValueType the to value type* @return the t*/public static <T> T convertValue(@NonNull Object fromValue, @NonNull Class<T> toValueType) {try {return objectMapper.convertValue(fromValue, toValueType);} catch (Exception e) {log.error("str2Obj fail");return null;}}/*** 对象互转泛型模式** @param <T> the type parameter* @param fromValue the from value* @param toValueTypeRef the to value type ref* @return the t*/public static <T> T convertValue(@NonNull Object fromValue, @NonNull TypeReference<T> toValueTypeRef) {try {return objectMapper.convertValue(fromValue, toValueTypeRef);} catch (Exception e) {log.error("str2Obj fail");return null;}}
}
测试Json转换
测试类及测试结果:
package com.example.springboot.jackson;import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;/*** @author tianmaolin004* @date 2023/3/18*/
public class JsonTest {public static void main(String[] args) {// 序列化操作List<String> interests = new ArrayList<>();interests.add("跑步");interests.add("游泳");Person person = Person.builder().name("tml").age(24).interests(interests).birthday(LocalDate.now()).build();System.out.println("obj2Str,序列化对象");System.out.println(JsonUtils.obj2Str(person));Person personLoseAttr = Person.builder().name("tml").interests(interests).birthday(LocalDate.now()).build();System.out.println("obj2Str,序列化缺失元素对象");System.out.println(JsonUtils.obj2Str(personLoseAttr));Person emptyPerson = new Person();System.out.println("obj2Str,序列化空对象");System.out.println(JsonUtils.obj2Str(emptyPerson));// 反序列化操作String personStr = "{\"name\":\"wc\",\"age\":99,\"interests\":[\"跑步\",\"游泳\",\"唱跳\",\"rapper\",\"打游戏\"],\"birthday\":\"2023-03-05\"}";System.out.println("str2Obj,反序列化对象");System.out.println(JsonUtils.str2Obj(personStr, Person.class));System.out.println("str2Obj,反序列化对象,由于字符串不符合Json的mapping格式,所以需要特殊判断");System.out.println(JsonUtils.str2Obj("北京", String.class));System.out.println("str2Obj,反序列化对象,由于集合不能指定元素类型,所以使用泛型方式");System.out.println(JsonUtils.str2Obj("[\"跑步\",\"游泳\",\"唱跳\",\"rapper\",\"打游戏\"]", new TypeReference<List<String>>() {}));String personLoseAttrStr = "{\"age\":99,\"interests\":[\"跑步\",\"游泳\",\"唱跳\",\"rapper\",\"打游戏\"]}";System.out.println("str2Obj,反序列化缺失元素的对象");System.out.println(JsonUtils.str2Obj(personLoseAttrStr, Person.class));String personBadAttrStr = "{\"ag\":99,\"interests\":[\"跑步\",\"游泳\",\"唱跳\",\"rapper\",\"打游戏\"]}";System.out.println("str2Obj,反序列化包含不存在元素的对象");System.out.println(JsonUtils.str2Obj(personBadAttrStr, Person.class));// 反序列化为jsonNode对象,获取属性值并将属性值转为目标类型对象:场景,目标JSON结构复杂,不想映射创建一个对象,只想使用其中部分数据,则先将JSON转为JsonNode对象,获取其部分属性值再转为我们需要的确定性对象System.out.println("str2JsonNode,反序列化JsonNode对象");JsonNode jsonNode = JsonUtils.str2JsonNode(personStr);System.out.println(jsonNode);System.out.println(jsonNode.findValues("age"));System.out.println(JsonUtils.convertValue(jsonNode.get("interests"), new TypeReference<List<String>>() {}));System.out.println(JsonUtils.convertValue(jsonNode.get("name"), String.class));}
}
打印结果如下:
应用场景:深拷贝
对于List中存在对象的情况下除了循环遍历,对每个元素重建外,通过序列化的方式也能轻松的实现集合的深拷贝
List<FeeRateDiscount> resultPlatDiscount = BeanCopyUtils.deepCopy(platDiscountFeeRates, new TypeReference<List<FeeRateDiscount>>() {});
public static <T> T deepCopy(Object src, TypeReference<T> dstClazz) {if (null == src) {return null;}return JsonUtils.str2Obj(JsonUtils.obj2Str(src), dstClazz);}
总结一下
在不知所以然的时候很容易用错一些最基础的知识,越是基础的知识其发生错误的情况所导致的影响范围也越大,只有踏踏实实的自己尝试过,才知道框架怎么用最好,所以对于新知识最好不要抱有模糊的侥幸心理,这样其实自己心里也不踏实,不敢实践。Jackson是个好的开始,接下来则是Jooq以及Gradle。
相关文章:

【工作中问题解决实践 七】SpringBoot集成Jackson进行对象序列化和反序列化
去年10月份以来由于公司和家里的事情太多,所以一直没有学习,最近缓过来了,学习的脚步不能停滞啊。回归正题,其实前年在学习springMvc的时候也学习过Jackson【Spring MVC学习笔记 五】SpringMVC框架整合Jackson工具,但是…...

香港服务器遭受DDoS攻击后如何恢复运行?
您是否发现流量异常上升?您的网站突然崩溃了吗?当您注意到这些迹象时,可能是在陷入了DDoS攻击的困境,因而,当开始考虑使用香港服务器时,也应该考虑香港服务器设备受DDoS攻击时,如何从中恢复。 在 DDoS 攻击香港…...

【Hive】配置
目录 Hive参数配置方式 参数的配置方式 1. 文件配置 2. 命令行参数配置 3. 参数声明配置 配置源数据库 配置元数据到MySQL 查看MySQL中的元数据 Hive服务部署 hiveserver2服务 介绍 部署 启动 远程连接 1. 使用命令行客户端beeline进行远程访问 metastore服务 …...
IP-GUARD如何强制管控电脑设置开机密码要符合密码复杂度?
如何强制管控电脑设置开机密码要符合密码复杂度? 7 可以在控制台-【策略】-【定制配置】,添加一条配置,开启系统密码复杂度检测。 类别:自定义 关键字:bp_password_complexity 内容:1 效果图:...
剑指 Offer II 031. 最近最少使用缓存
题目链接 剑指 Offer II 031. 最近最少使用缓存 mid 题目描述 运用所掌握的数据结构,设计和实现一个 LRU(Least Recently Used,最近最少使用) 缓存机制 。 实现 LRUCache类: LRUCache(int capacity)以正整数作为容量 capacity初始化 LRU缓…...

44岁了,我从没想过在CSDN创作2年,会有这么大收获
1998年上的大学,02年毕业,就算从工作算起,我也有20余年的码龄生涯了。 但正式开启博文的写作,却是2021年开始的,差不多也就写了2年的博客,今天我来说说我在CSDN的感受和收获。 我是真的没想到,…...

相位相参信号源的设计--示波器上的信号不稳定,来回跑?
目录乱跑的波形边沿触发触发方式外部触发相参与非相参相位相参的射频信号源样机外观与内部设计软件设计上位机软件信号源使用方法PWM触发信号射频信号的时域波形射频信号的频谱输出功率在示波器的实际使用当中波形在示波器的时域上乱跑,左右移动,定不下来…...
Spring Boot 整合 RabbitMQ 多种消息模式
Spring Boot 整合 RabbitMQ 多种消息模式 准备工作集成 RabbitMQ发布/订阅模式点对点模式主题模式总结Spring Boot 是一个流行的 Java 应用程序开发框架,而 RabbitMQ 是一款可靠的消息队列软件。将 Spring Boot 和 RabbitMQ 结合起来可以帮助我们轻松地实现异步消息传递。Rabb…...

node多版本控制
前言 最近在折腾Python,并将node升级至v18.14.2。突然发现一个旧项目无法运行,也无法打包,里面的node-sass报错,显然这是因为node版本过高导致的。 将node版本降低至以前的v14.16.0,果然立马就能正常运行。 存在不同…...
Redis set集合
Redis set (集合)遵循无序排列的规则,集合中的每一个成员(也就是元素,叫法不同而已)都是字符串类型,并且不可重复。Redis set 是通过哈希映射表实现的,所以它的添加、删除、查找操作…...

漫画:什么是希尔排序算法?
希尔排序(ShellSort)是以它的发明者Donald Shell名字命名的,希尔排序是插入排序的改进版,实现简单,对于中等规模数据的性能表现还不错 一、排序思想 前情回顾:漫画:什么是插入排序算法…...

问卷工具选择要看哪些方面?
通常来讲,我们在使用一款问卷制作工具制作问卷时会有哪些需求呢? 一、用户需求 1、操作简单,易上手。 2、能够满足用户个性化的需求。 3、提供多语言服务。 4、能够帮助发布以及数据收集。 5、简化数据分析 市面上的问卷调查制作工具都…...

Qt之QPainter绘制多个矩形/圆形(含源码+注释)
一、绘制示例图 下图绘制的是矩形对象,但是将绘制矩形函数(drawRect)更改为绘制圆形(drawEllipse)即可绘制圆形。 二、思路解释 绘制矩形需要自然要获取矩形数据,因此通过鼠标事件获取每个矩形的rect数…...

介绍两款红队常用的信息收集组合工具
介绍两款红队常用的信息收集组合工具1.Ehole本地识别FOFA识别结果输出2.AlliN1.Ehole EHole(棱洞)3.0 红队重点攻击系统指纹探测工具 EHole是一款对资产中重点系统指纹识别的工具,在红队作战中,信息收集是必不可少的环节,如何才能从大量的资…...

类ChatGPT国产大模型ChatGLM-6B,单卡即可运行
2023年3月14日GPT4又发布了,在ChatGPT发展如火如荼的当下,我们更应该关注国内的进展,今天将分享一个清华大学基于GLM-130B模型开发的类似ChatGPT的ChatGLM-6B模型,ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型࿰…...

vue的diff算法?
文章目录是什么比较方式原理分析Diff算法的步骤:首尾指针法比对顺序:是什么 diff 算法是一种通过同层的树节点进行比较的高效算法 其有两个特点: 比较只会在同层级进行, 不会跨层级比较 在diff比较的过程中,循环从两边向中间比较…...

C++ | 对比inline内联函数和宏的不同点
文章目录一、前言二、宏的优缺点分析1、概念回顾2、宏的缺点3、宏的优点三、inline内联函数1、概念2、特性①:空间换时间🎁趣味杂谈:庞大的游戏更新包3、特性②:inline实现机制4、特性③:inline的声明与定义反汇编观察…...

面试官问 : ArrayList 不是线程安全的,为什么 ?(看完这篇,以后反问面试官)
前言 金三银四 ? 也许,但是。 近日,又收到金三银四一线作战小队成员反馈的战况 : 我不管你从哪里看的面经,但是我不允许你看到我这篇文章之后,还不清楚这个面试问题。 本篇内容预告: Array…...

Linux串口应用编程
一、 串口API 在Linux系统中,操作设备的统一接口就是:open/ioctl/read/write。 对于UART,又在ioctl之上封装了很多函数,主要是用来设置行规程。 所以对于UART,编程的套路就是: open设置行规程,比如波特率、数据位、停止位、检验位、RAW模式、一有数据就返回read/write 怎么设置…...
java程序员学前端-HTML篇
HTML 与 CSS HTML 是什么:即 HyperText Markup language 超文本标记语言,咱们熟知的网页就是用它编写的,HTML 的作用是定义网页的内容和结构。 HyperText 是指用超链接的方式组织网页,把网页联系起来Markup 是指用 <标签>…...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...