【Java】Jackson序列化案例分析

1.Jackson介绍

Jackson 是一个流行的 Java 库,用于处理 JSON 数据。它提供了高效的序列化和反序列化功能,能够将 Java 对象转换为 JSON 格式,反之亦然。
它由 FasterXML 开发和维护。Jackson 的设计目标是提供高效、灵活且易于使用的 JSON 处理功能。它广泛应用于各种 Java 项目和框架中,成为处理 JSON 的事实标准之一。

  • 序列化是将 Java 对象转换为 JSON 字符串的过程。在 Jackson 中,这个过程通常由 ObjectMapper 类来完成。ObjectMapper 是 Jackson 的核心类,提供了许多方法来进行序列化操作。
  • 反序列化是将 JSON 字符串转换为 Java 对象的过程。在 Jackson 中,这个过程同样由 ObjectMapper 类来完成。

本文主要介绍Jaskson的序列化(writeValueAsString),涉及时间格式的自定义序列化,字符串的自定义序列化

2.案例1 将User转换为Json字符串(对时间特殊处理)

尝试
Bean类,设置不同的数据类型。

import lombok.Data; // get/set方法
import java.time.LocalDateTime;
import java.util.Date;

@Data 
public class UserBean {

    private String name;

    private Integer age;
    
    private Date birthday;

    private LocalDateTime createTime;

    private Boolean isDelete;
}

测试类:测试Jackson的序列化功能,即将Java对象转为Json字符串

public class TestJackson {
    
   public static UserBean getBean(){
        UserBean userBean = new UserBean();
        userBean.setName("zhi");
        userBean.setAge(18);
        userBean.setBirthday(new Date());
        userBean.setCreateTime(LocalDateTime.now());
        userBean.setIsDelete(false);
        return userBean;
    }

    public static void main(String[] args) {
        UserBean user = getBean();
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String userJsonStr = objectMapper.writeValueAsString(user);
            System.out.println(userJsonStr);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

输出:

{"name":"zhi","age":18,"birthday":1735125463752,"createTime":{"dayOfWeek":"WEDNESDAY","dayOfYear":360,"year":2024,"month":"DECEMBER","nano":784000000,"monthValue":12,"dayOfMonth":25,"hour":19,"minute":17,"second":43,"chronology":{"calendarType":"iso8601","id":"ISO"}},"isDelete":false}

发现时间格式可读性差,想转换为2024-12-25 19:04:19类型。

修改:在实体类中添加注解

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Data
public class UserBean {

    private String name;

    private Integer age;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date birthday;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;

    private Boolean isDelete;
}

再次运行并输出

{"name":"zhi","age":18,"birthday":"2024-12-25 19:22:12","createTime":{"dayOfWeek":"WEDNESDAY","dayOfYear":360,"year":2024,"month":"DECEMBER","nano":851000000,"monthValue":12,"dayOfMonth":25,"hour":19,"minute":22,"second":12,"chronology":{"calendarType":"iso8601","id":"ISO"}},"isDelete":false}

发现birthday对了,但createTime不行。

继续修改

创建自定义LocalDateTime的序列化器:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
/**
 * 自定义LocalDateTime的序列化器
 * 该序列化器用于将LocalDateTime对象转换为符合特定格式的JSON字符串
 */
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

    // 定义日期时间格式器,用于将LocalDateTime格式化为字符串
    // 格式为:年-月-日 时:分:秒 时区,时区为GMT+8
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of("GMT+8"));

    /**
     * 序列化LocalDateTime对象
     *
     * @param localDateTime 待序列化的LocalDateTime对象
     * @param jsonGenerator 用于生成JSON数据的工具
     * @param serializerProvider 提供序列化器的工具,通常未使用
     * @throws IOException 当JSON生成过程中发生错误时抛出
     */
    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        // 将LocalDateTime转换为带有GMT+8时区的ZonedDateTime对象
        ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("GMT+8"));
        // 使用定义好的格式器将ZonedDateTime格式化为字符串,并写入JSON
        jsonGenerator.writeString(zonedDateTime.format(formatter));
    }
}

在实体类中添加注解@JsonSerialize(using = XXX.class)

@Data
public class UserBean {

    private String name;

    private Integer age;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date birthday;

    @JsonSerialize(using = LocalDateTimeSerializer.class)
    private LocalDateTime createTime;

    private Boolean isDelete;
}

在此运行测试方法

{"name":"zhi","age":18,"birthday":"2024-12-25 19:31:20","createTime":"2024-12-25 19:31:20","isDelete":false}

成功!

不用注解的实现

两个序列化器

public class DateSerializer extends JsonSerializer<Date> {

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of("GMT+8"));

    @Override
    public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        // 将Date对象转换为ZonedDateTime对象
        ZonedDateTime zonedDateTime = date.toInstant().atZone(ZoneId.of("GMT+8"));
        // 将ZonedDateTime对象格式化为字符串
        String formattedDate = formatter.format(zonedDateTime);
        // 将格式化后的日期时间字符串写入JSON生成器
        jsonGenerator.writeString(formattedDate);
    }
}

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

    // 定义日期时间格式器,用于将LocalDateTime格式化为字符串
    // 格式为:年-月-日 时:分:秒 时区,时区为GMT+8
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of("GMT+8"));

    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        // 将LocalDateTime转换为带有GMT+8时区的ZonedDateTime对象
        ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("GMT+8"));
        // 使用定义好的格式器将ZonedDateTime格式化为字符串,并写入JSON
        jsonGenerator.writeString(zonedDateTime.format(formatter));
    }
}

实体类

 */
@Data
public class UserBean {

    private String name;

    private Integer age;

    private Date birthday;

    private LocalDateTime createTime;

    private Boolean isDelete;
}

测试方法

public class TestJackson {

   public static UserBean getBean(){
        UserBean userBean = new UserBean();
        userBean.setName("zhi");
        userBean.setAge(18);
        userBean.setBirthday(new Date());
        userBean.setCreateTime(LocalDateTime.now());
        userBean.setIsDelete(false);
        return userBean;
    }

    public static void main(String[] args) {
        UserBean user = getBean();
        ObjectMapper objectMapper = new ObjectMapper();
        // 配置ObjectMapper以处理LocalDateTime、Date类型的序列化
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        javaTimeModule.addSerializer(Date.class, new DateSerializer());
        objectMapper.registerModule(javaTimeModule);
        try {
            String userJsonStr = objectMapper.writeValueAsString(user);
            System.out.println(userJsonStr);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

输出:
在这里插入图片描述
成功!

需求2 手机号特殊处理

接需求1,在User中添加手机号

@Data
public class UserBean {

    private String name;

    private String phone;

    private Integer age;

    private Date birthday;
    
    private LocalDateTime createTime;

    private Boolean isDelete;
}

需求:序列化时将手机号中间四位隐藏

添加对手机号字符串的处理

public class PhoneSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (str == null || str.length() != 11) {
            jsonGenerator.writeString(str);
        } else {
            // 将手机号中间四位替换为*
            String maskedPhone = str.substring(0, 3) + "****" + str.substring(7);
            jsonGenerator.writeString(maskedPhone);
        }
    }
}

测试:创建SimpleModule并注册PhoneSerializer

public class TestJackson {

   public static UserBean getBean(){
        UserBean userBean = new UserBean();
        userBean.setName("zhi");
        userBean.setPhone("18823452345");
        userBean.setAge(18);
        userBean.setBirthday(new Date());
        userBean.setCreateTime(LocalDateTime.now());
        userBean.setIsDelete(false);
        return userBean;
    }

    public static void main(String[] args) {
        UserBean user = getBean();
        ObjectMapper objectMapper = new ObjectMapper();
        // 配置ObjectMapper以处理LocalDateTime、Date类型的序列化
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        javaTimeModule.addSerializer(Date.class, new DateSerializer());
        objectMapper.registerModule(javaTimeModule);

        // 创建SimpleModule并注册PhoneSerializer
        SimpleModule phoneModule = new SimpleModule();
        phoneModule.addSerializer(String.class, new PhoneSerializer());
        objectMapper.registerModule(phoneModule);

        try {
            String userJsonStr = objectMapper.writeValueAsString(user);
            System.out.println(userJsonStr);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

输出:

{“name”:“zhi”,“phone”:“188****2345”,“age”:18,“birthday”:“2024-12-26 18:39:04”,“createTime”:“2024-12-26 18:39:04”,“isDelete”:false}

成功了,但是有个问题,我们的PhoneSerializer处理的是字符串类型,而不是固定为phone的字符串

假如User中有两个手机号,我们只对其中一个做处理

@Data
public class UserBean {

    private String name;

    private String phone;

    private String phone1;

    private Integer age;

    private Date birthday;
    
    private LocalDateTime createTime;

    private Boolean isDelete;
}

方法1:直接在UserBean中为特定字段添加注解

    @JsonSerialize(using = PhoneSerializer.class)
    private String phone;

PhoneSerializer序列化器

public class PhoneSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (str == null || str.length() != 11) {
            jsonGenerator.writeString(str);
        } else {
            // 将手机号中间四位替换为*
            String maskedPhone = str.substring(0, 3) + "****" + str.substring(7);
            jsonGenerator.writeString(maskedPhone);
        }
    }
}

方法2:不使用注解

手机号序列化器保存不变,但只对phone字段添加该手机号序列化器。

自定义Bean序列化修改器,用于修改特定属性的序列化方式,例如对电话号码进行特殊格式化处理

/**
 * 自定义Bean序列化修改器
 * 用于修改特定属性的序列化方式,例如对电话号码进行特殊格式化处理
 */
public class CustomBeanSerializerModifier extends BeanSerializerModifier {

    // 自定义的电话号码序列化器
    private final JsonSerializer<Object> phoneSerializer = new JsonSerializer<Object>() {
        // 委托实际的序列化工作给PhoneSerializer
        private final PhoneSerializer delegate = new PhoneSerializer();

        /**
         * 序列化电话号码属性
         * 如果值是字符串类型,则使用PhoneSerializer进行序列化
         * 否则,使用默认方式序列化值
         *
         * @param value 要序列化的值
         * @param gen Json生成器,用于输出Json数据
         * @param serializers 序列化提供者,用于获取其他序列化器
         * @throws IOException 如果序列化过程中发生IO错误
         */
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            if (value instanceof String) {
                delegate.serialize((String) value, gen, serializers);
            } else {
                gen.writeObject(value);
            }
        }
    };

    /**
     * 自定义修改Bean属性的序列化方式
     *
     * @param config 当前的序列化配置
     * @param beanDesc Bean描述信息,包含Bean的结构和属性
     * @param beanProperties 原始的Bean属性写入器列表
     * @return 返回修改后的Bean属性写入器列表
     */
    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
        // 遍历所有Bean属性写入器
        for (BeanPropertyWriter writer : beanProperties) {
            // 检查当前属性是否为"phone"
            if ("phone".equals(writer.getName())) {
                // 为"phone"属性指定自定义的序列化器
                writer.assignSerializer(phoneSerializer);
            }
        }
        // 返回修改后的Bean属性写入器列表
        return beanProperties;
    }
}

测试, 注册该自定义的Bean序列化修饰器模块

public class TestJackson {

   public static UserBean getBean(){
        UserBean userBean = new UserBean();
        userBean.setName("zhi");
        userBean.setPhone("18823452345");
        userBean.setPhone1("18823452345");
        userBean.setAge(18);
        userBean.setBirthday(new Date());
        userBean.setCreateTime(LocalDateTime.now());
        userBean.setIsDelete(false);
        return userBean;
    }

    public static void main(String[] args) {
        UserBean user = getBean();
        ObjectMapper objectMapper = new ObjectMapper();
        // 配置ObjectMapper以处理LocalDateTime、Date类型的序列化
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        javaTimeModule.addSerializer(Date.class, new DateSerializer());
        objectMapper.registerModule(javaTimeModule);

        // 注册自定义的Bean序列化修饰器模块
        SimpleModule module = new SimpleModule();
        module.setSerializerModifier(new CustomBeanSerializerModifier());
        objectMapper.registerModule(module);

        try {
            String userJsonStr = objectMapper.writeValueAsString(user);
            System.out.println(userJsonStr);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

输出:
{“name”:“zhi”,“phone”:“188****2345”,“phone1”:“18823452345”,“age”:18,“birthday”:“2024-12-26 19:11:38”,“createTime”:“2024-12-26 19:11:38”,“isDelete”:false}

成功!

总结

推荐注解的方式针对各别字段注册序列化器。

public class UserBean {

    private String name;

    @JsonSerialize(using = PhoneSerializer.class)
    private String phone;

    private String phone1;

    private Integer age;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date birthday;

    @JsonSerialize(using = LocalDateTimeSerializer.class)
    private LocalDateTime createTime;

    private Boolean isDelete;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java反射学习(2)(“反射“机制获取构造方法及内部信息(Constructor类))

目录 一、"Class"对象实例化的常见三种方式以及使用时机。 &#xff08;1&#xff09;源代码(编写)阶段——使用全限定类名.forName()。 &#xff08;2&#xff09;加载阶段——使用类名.class。 &#xff08;3&#xff09;运行阶段——使用对象.getClass()。 二、Ja…

洛谷 P1595 信封问题 C语言dp

题目描述 某人写了 n 封信和 n 个信封&#xff0c;如果所有的信都装错了信封。求所有信都装错信封共有多少种不同情况。 输入格式 一个信封数 n&#xff0c;保证 n≤20。 输出格式 一个整数&#xff0c;代表有多少种情况。 输入输出样例 输入 #1 2 输出 #1 1 输入 #2 3 输…

【LuaFramework】服务器模块相关知识

目录 一、客户端代码 二、本地服务器代码 三、解决服务器无法多次接收客户端消息问题 一、客户端代码 连接本地服务器127.0.0.1:2012端口&#xff08;如何创本地服务器&#xff0c;放最后说&#xff09;&#xff0c;连接成功后会回调 协议号Connect是101&#xff0c;其他如下…

解决Ascend上vllm运行时出现urllib3.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED]

背景 尝试使用vllm模型&#xff0c;脚本代码如下&#xff1a; from vllm import LLM, SamplingParamsprompts ["Hello, my name is","The president of the United States is","The capital of France is","The future of AI is", …

【卷积神经网络】常用评价指标总结

评估指标 概述 该评价指标适合分类任务与目标检测&#xff0c;主要用于评估模型的性能。该文章对相关指标进行总结&#xff0c;同时对输出的图片进行学习分析 混淆矩阵的组成 TP&#xff08;True Positives&#xff0c;真正例&#xff09;&#xff1a;实际为正例&#xff0c;…

HarmonyOS NEXT 的技术发展和市场趋势:打造1+8+N的万物互联新世界

随着5G、AI、物联网等技术的飞速发展&#xff0c;全球智能设备和操作系统的竞争也日益激烈。在这一背景下&#xff0c;华为推出的HarmonyOS NEXT正逐渐成为智能设备生态中一个重要的参与者&#xff0c;其独特的18N战略布局以及跨设备、跨平台的互联互通理念&#xff0c;正在塑造…

免费 IP 归属地接口

免费GEOIP&#xff0c;查询IP信息&#xff0c;支持IPV4 IPV6 ,包含国家地理位置&#xff0c;维度&#xff0c;asm,邮编 等&#xff0c;例如 例如查询1.1.1.1 http://geoip.91hu.top/?ip1.1.1.1 返回json 对象

以太网通信--读取物理层PHY芯片的状态

PHY芯片通过MDIO接口进行读写&#xff0c;框图如下所示&#xff1a; 原理很简单&#xff0c;就是按照时序将PHY芯片的指定寄存器信息读出或者写入。 MDC时钟需要输出到PHY芯片&#xff0c;一般不低于80MHz。 MDIO是双向接口&#xff0c;FPGA读出状态信息时为输入&#xff0c;FP…

Docker服务发现新纪元:探索Consul的无限魅力

作者简介&#xff1a;我是团团儿&#xff0c;是一名专注于云计算领域的专业创作者&#xff0c;感谢大家的关注 •座右铭&#xff1a; 云端筑梦&#xff0c;数据为翼&#xff0c;探索无限可能&#xff0c;引领云计算新纪元个人主页&#xff1a;团儿.-CSDN博客 目录 前言&…

OpenHarmony-6.IPC/RPC组件

IPC/RPC组件机制 1.基本概念 IPC&#xff1a;设备内的进程间通信&#xff08;Inter-Process Communication&#xff09;。 RPC&#xff1a;设备间的进程间通信&#xff08;Remote Procedure Call&#xff09;。 IPC/RPC用于实现跨进程通信&#xff0c;不同的是前者使用Binder驱…

0.机顶盒晶晨s905l3b芯片--刷入第三方系统+安卓9 root教程+armbian写入EMMC教程

机顶盒s905l3b芯片刷第三方系统安卓9 root教程刷armbian写入EMMC教程 声明&#xff1a; 由于固件、软件、镜像等持续更新&#xff0c;本文仅代表当前所使用版本的流畅安装记录。行文略长&#xff0c;关键代码处会配以截图展示&#xff0c;请自行对比是否存在差异导致安装失败…

视频监控平台:Liveweb视频汇聚融合平台智慧安防视频监控应用方案

Liveweb是一款功能强大、灵活部署的安防视频监控平台&#xff0c;支持多种主流标准协议&#xff0c;包括GB28181、RTSP/Onvif、RTMP等&#xff0c;同时兼容海康Ehome、海大宇等厂家的私有协议和SDK接入。该平台不仅提供传统安防监控功能&#xff0c;还支持接入AI智能分析&#…

汇编语言学习

想要理解栈溢出的最基本原理&#xff0c;汇编和栈是必不可少的&#xff0c;不然想我之前学了也是白学&#xff0c;原理都不知道 一、准备 1.安装gcc sudo apt-get build-dep gcc 这里显示版本不对&#xff0c;我用的是国内镜像源&#xff0c;需要换一下配置 sudo nano /e…

“乡村探索者”:村旅游网站的移动应用开发

3.1 可行性分析 从三个不同的角度来分析&#xff0c;确保开发成功的前提是有可行性分析&#xff0c;只有进行提前分析&#xff0c;符合程序开发流程才不至于开发过程的中断。 3.1.1 技术可行性 在技术实现层次&#xff0c;分析了好几种技术实现方法&#xff0c;并且都有对应的成…

Python + 深度学习从 0 到 1(02 / 99)

希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【深度学习从 0 到 1】谢谢你的支持&#xff01; ⭐ Keras 快速入门&#xff1a; 神经网络的基本数据结…

MySQL用户授权

什么是数据库 数据库概述&#xff1a;数据库是按照一定的数据结构将数据存储在存储器的集合常见数据库软件 软件名开源跨平台厂 商Oracle否是甲骨文MySQL是是甲骨文SQL Server否否微软DB2否是IBMMongoDB是是MongoDB Inc.Redis是是开源软件Memcached是是开源软件 DB (DataBas…

2025年我国网络安全发展形势展望

展望2025年&#xff0c;我国网络安全产业有望迎来新的快速增长阶段&#xff0c;零信任安全架构将在各行各业加快应用落地&#xff0c;数据安全技术攻关和应用进程加快&#xff0c;关键基础设施安全能力不断提升。同时&#xff0c;也应关注国家级网络对抗风险加剧、网络安全产业…

GitPuk安装配置指南

GitPuk是一款开源免费的代码管理工具&#xff0c;上篇文章已经介绍了Gitpuk的功能与优势&#xff0c;这篇文章将为大家讲解如何快速安装和配置GitPuk&#xff0c;助力你快速的启动GitPuk管理代码 1. 安装 支持 Windows、Mac、Linux、docker 等操作系统。 1.1 Windows安装 下载…

ArcGIS+MIKE21 洪水淹没分析、溃坝分析,洪水淹没动态效果

洪水淹没分析过程&#xff1a; 一、所需数据&#xff1a; 1.分析区域DEM数据 二、ArcGIS软件 1.提取分析区域DEM&#xff08;水库坝下区域&#xff09; 2.DEM栅格转点 3.计算转换后几何点的x和y坐标值&#xff08;精度20、小数位3&#xff09; 4.导出属性表&#xff0c;形式…

中伟视界:AI识别摄像头+AI预警平台在矿山皮带空载监测中的应用

在矿山开采和矿物处理过程中&#xff0c;皮带运输机扮演着举足轻重的角色。它们负责将矿石、煤炭等物料从一处运送到另一处&#xff0c;是矿山生产流程中不可或缺的一环。然而&#xff0c;皮带运输机在运行过程中也面临着一些挑战&#xff0c;其中之一便是皮带空载问题。皮带空…