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

【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+8private 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生成过程中发生错误时抛出*/@Overridepublic void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {// 将LocalDateTime转换为带有GMT+8时区的ZonedDateTime对象ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("GMT+8"));// 使用定义好的格式器将ZonedDateTime格式化为字符串,并写入JSONjsonGenerator.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"));@Overridepublic 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+8private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of("GMT+8"));@Overridepublic void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {// 将LocalDateTime转换为带有GMT+8时区的ZonedDateTime对象ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("GMT+8"));// 使用定义好的格式器将ZonedDateTime格式化为字符串,并写入JSONjsonGenerator.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> {@Overridepublic 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并注册PhoneSerializerSimpleModule 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> {@Overridepublic 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>() {// 委托实际的序列化工作给PhoneSerializerprivate final PhoneSerializer delegate = new PhoneSerializer();/*** 序列化电话号码属性* 如果值是字符串类型,则使用PhoneSerializer进行序列化* 否则,使用默认方式序列化值** @param value 要序列化的值* @param gen Json生成器,用于输出Json数据* @param serializers 序列化提供者,用于获取其他序列化器* @throws IOException 如果序列化过程中发生IO错误*/@Overridepublic 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属性写入器列表*/@Overridepublic 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;
}

http://www.mrgr.cn/news/81537.html

相关文章:

  • 关于在M系列的Mac中使用SoftEtherClient软件
  • oracle 加字段和字段注释 sql
  • React 第十九节 useLayoutEffect 用途使用技巧注意事项详解
  • 2. FPGA基础了解--全局网络
  • 神经网络-VggNet
  • PCL-计算点云AABB包围盒
  • 使用Grafana中按钮插件实现收发HTTP请求
  • 前端登录业务
  • 企业内训|高智能数据构建和多模态数据处理、Agent研发及AI测评技术内训-吉林省某汽车厂商
  • Chapter 03 复合数据类型-1
  • K8S部署CloudNativePG,忘记设置密码,修改管理员密码解决方案
  • [c++进阶(三)]单例模式及特殊类的设计
  • 安装k8s涉及命令(方便放到txt离线使用)
  • 攻防世界web第二题unseping
  • C++模板:编译时模拟Duck Typing
  • LLaMA-Factory GLM4-9B-CHAT LoRA 指令微调实战
  • 【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
  • 【stm32can】
  • CSharp: Oracle Stored Procedure query table
  • 重温设计模式--10、单例模式
  • STM32项目之环境空气质量检测系统软件设计
  • 【Git】-- 版本说明
  • DX12 快速教程(2) —— 渲染天蓝色窗口
  • 笔记本通过HDMI转VGA线连接戴尔显示器,wifi不可用或网速变慢
  • 大数据实验二
  • 鸿蒙之路的坑