【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;
}