注:笔者只是挑了自己需要理解的一部分内容,详细内容请看以下文章
引用:
spring:基于SimpleModule实现动态管理jackson的序列化器(JsonSerializer)和反序列化器(JsonDeserializer)
1JSON是什么
- JSON,全称是 JavaScript Object Notation,即 JavaScript对象标记法。
- JSON是一种轻量级(Light-Meight)、基于文本的(Text-Based)、可读的(Human-Readable)格式。
- JSON无论对于人,还是对于机器来说,都是十分便于阅读和书写的,而且相比 XML(另一种常见的数据交换格式),文件更小,因此迅速成为网络上十分流行的交换格式。
2. JSON 的语法规则是怎样的?
JSON 的语法规则十分简单,可称得上“优雅完美”,总结起来有:
数组(Array)用方括号(“[]”)表示。
对象(0bject)用大括号(“{}”)表示。
名称/值对(name/value)组合成数组和对象。
名称(name)置于双引号中,值(value)有字符串、数值、布尔值、null、对象和数组。
并列的数据之间用逗号(“,”)分隔
值的范围
Number:数字(整数或浮点数)
String:字符串(在双引号中),一定是英文双引号(“”),个别弱语言可以支持单引号。
Boolean:逻辑值(true 或 false)
Array:数组(在方括号中),一般是在Value位置上。
Object:对象(在花括号中),一般是在Value位置上。
null:没什么好说的。
{
"name": "xdr630",
"favorite": "programming"
}
3. JSON的解析和生成(JSON 和 JS 对象互转)
在JavaScript中,有两个方法与此相关: JSON.parse和 JSON.stringify 。
JSON 和 JS 对象互转
要实现从JSON字符串转换为JS对象,使用 JSON.parse() 方法:
要实现从JS对象转换为JSON字符串,使用 JSON.stringify() 方法:
4.Java JSON 库
Java 中比较流行的 JSON 库有:
Fastjson - 阿里巴巴开发的 JSON 库,性能十分优秀。
Jackson - 社区十分活跃且更新速度很快。Spring 框架默认 JSON 库。
Gson - 谷歌开发的 JSON 库,目前功能最全的 JSON 库 。
从性能上来看,一般情况下:Fastjson > Jackson > Gson
快速上手
1 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
2 实体类
package com.geekmice.springbootselfexercise.domain;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelIgnore;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserDomain implements Serializable {
/**
* 用户名
*/
private String userName;
/**
* 生日
*/
private Date birthday;
/**
* 性别
*/
private String sex;
/**
* 地址
*/
private String address;
}
3 测试类
/**
* 处理fastjson
*/
@Test
public void validateFastJson() {
UserDomain user = UserDomain.builder()
.userName("胡汉三").sex("男")
.birthday(new Date()).address("123@163.com").build();
String userJsonString = JSON.toJSONString(user);
log.info("userJsonString : [\n{}\n]", userJsonString);
}
5 常见用法
1 序列化操作
序列化:将一个对象编码成一个字节流(I/O),序列化的目的是为了方便数据的传递以及存储到磁盘上(把一个Java对象写入到硬盘或者传输到网路上面的其它计算机,这时我们就需要将对象转换成字节流才能进行网络传输。对于这种通用的操作,就出现了序列化来统一这些格式)。
核心操作
/**
* This method serializes the specified object into its equivalent Json representation. Note that this method works fine if the any of the object fields are of generic type,
* just the object itself should not be of a generic type. If you want to write out the object to a
* {@link Writer}, use {@link #writeJSONString(Writer, Object, SerializerFeature[])} instead.
*
* @param object the object for which json representation is to be created setting for fastjson
* @return Json representation of {@code object}.
*/
String jsonString = JSON.toJSONString(obj);
对象转换为JSON串
/**
* 处理fastjson
*/
@Test
public void validateFastJson() {
log.info("序列化操作开始,对象转换JSON串");
UserDomain user = UserDomain.builder()
.userName("胡汉三").sex("男")
.birthday(new Date()).address("123@163.com").build();
String userJsonString = JSON.toJSONString(user);
log.info("userJsonString : [\n\n{}\n\n]", userJsonString);
}
14:59:16.377 [main] INFO com.geekmice.springbootselfexercise.NoDaoTest - userJsonString : [
{“address”:“123@163.com”,“birthday”:1691564356144,“sex”:“男”,“userName”:“胡汉三”}
]
list转换JSON串
/**
* 处理fastjson
*/
@Test
public void validateFastJson() {
log.info("序列化操作开始,list转换JSON串");
List<UserDomain> result = Arrays.asList(
UserDomain.builder()
.userName("胡汉三").sex("男")
.birthday(new Date()).address("123@163.com").build(),
UserDomain.builder()
.userName("笑笑").sex("女")
.birthday(new Date()).address("345@163.com").build()
);
String listStr = JSON.toJSONString(result);
log.info("listStr : [\n\n{}\n\n]" , listStr);
}
14:59:16.381 [main] INFO com.geekmice.springbootselfexercise.NoDaoTest - listStr : [
[{“address”:“123@163.com”,“birthday”:1691564356380,“sex”:“男”,“userName”:“胡汉三”},{“address”:“345@163.com”,“birthday”:1691564356380,“sex”:“女”,“userName”:“笑笑”}]
]
map转换为JSON串
/**
* 处理fastjson
*/
@Test
public void validateFastJson() {
log.info("序列化操作开始,map转换JSON串");
HashMap<Object, Object> map = Maps.newHashMap();
map.put("name","小三子");
map.put("age",10);
final String mapStr = JSON.toJSONString(map);
log.info("mapStr : [\n\n{}\n\n]" , mapStr);
}
2 反序列化操作
JSON串转换为对象
String userJsonString="{\"address\":\"123@163.com\",\"birthday\":1691564927544,\"sex\":\"男\",\"userName\":\"胡汉三\"}";
log.info("反序列化开始,JSON串转换对象");
UserDomain nonUser = JSON.parseObject(userJsonString, UserDomain.class);
log.info("nonUser : [{}]" , nonUser);
JSON串转换为map
String mapStr="{\"name\":\"小三子\",\"age\":10}";
log.info("反序列化开始,JSON串转换map");
Map<Object, Object> nonMap = JSON.parseObject(mapStr, new TypeReference<Map<Object, Object>>() {
});
log.info("nonMap : [{}]" , nonMap);
JSON串转换为list
String listStr ="{\"address\":\"123@163.com\",\"birthday\":1691564927840,\"sex\":\"男\",\"userName\":\"胡汉三\"},{\"address\":\"345@163.com\",\"birthday\":1691564927840,\"sex\":\"女\",\"userName\":\"笑笑\"}"
log.info("反序列化开始:JSON串转换为list");
List<UserDomain> nonUserList = JSON.parseArray(listStr, UserDomain.class);
log.info("nonUserList : [{}]" , nonUserList);
如何处理日期毫秒值问题
方案一:使用 @JSONField(format = DateUtils.DATE_FORMAT_10)
format属性指定时间日期格式,只是针对于某几个字段,使用了这个注解有效
方案二:通过代码实现,这种形式所有date类型都是指定时间格式 yyyy-MM-dd
String result = JSON.toJSONStringWithDateFormat(user, com.alibaba.excel.util.DateUtils.DATE_FORMAT_10);
6.SimpleModule
jackson的(com.fasterxml.jackson.databind.Module)设计作为一个扩展的接口,可以注册到ObjectMapper实例(ObjectMapper.registerModule),为默认ObjectMapper实例提供功能扩展;比如用于定义为数据类型指定序列化和反序列化。
jackson为Module接口提供了一个默认的简单实现(com.fasterxml.jackson.databind.module.SimpleModule),SimpleModule已经很好用了,一般情况下可以直接拿来用。
jackson的Module机制是可以支持动态修改Module实例的,并且只要将Module注册到ObjectMapper,再修改Module实例的结果会同步到ObjectMapper实例。
还以上面的增加序列化器的过程为例,
SimpleModule中管理序列化器的字段为_serializers,类型为SimpleSerializers
ObjectMapper.registerModule注册Module的过滤其实就是将_serializers字段的实例增加到
ObjectMapper的SerializerFactory实例(_serializerFactory字段)public void addSerializers(Serializers s) { _serializerFactory = _serializerFactory.withAdditionalSerializers(s); }
可以先将
SimpleModule
注册到ObjectMapper
,再调用SimpleModule.addSerializer
方法也是同样可以将JsonSerializer
实例传递给已经注册的ObjectMapper
实例的。
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.DeserializeContextCacheCleaner;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.module.SimpleSerializers;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
/**
* 动态管理序列化器和反序列化器实现
* @author guyadong
*/
@Component
public class ExampleOfSimpleModule {
/**
* [自动注入]spring环境下的ObjectMapper实例
*/
@Autowired
private ObjectMapper objectMapper;
private final SimpleModule module = new SimpleModule();
@PostConstruct
void init() {
/**
* SimpleModule中的 _serializers和 _deserializers字段默认为空,如果不事先调用setter方法指定非空实例,
* 则执行 ObjectMapper.registerModule方法无效,
* 后续动态增加的序列化和反序列化器也无效
*/
module.setSerializers( new SimpleSerializers());
module.setDeserializers(new SimpleDeserializers());
objectMapper.registerModule(module);
}
/**
* 向 {@link #module}增加过反序列化器
* @param deserializer
*/
@SuppressWarnings("unchecked")
public void addDeserializer(JsonDeserializer<?> deserializer) {
if(null != deserializer) {
module.addDeserializer((Class<Object>)(deserializer).handledType(), deserializer);
/** 清除 DeserializationContext中的反序列化器缓存,否则增加的过滤器不一定能生效 */
DeserializeContextCacheCleaner.clearCache(objectMapper.getDeserializationContext());
}
}
/**
* 向 {@link #module}增加过序列化器
* @param serializer
*/
public void addSerializer(JsonSerializer<?> serializer) {
if(null != serializer) {
module.addSerializer(serializer);
/** 清除 DefaultSerializerProvider中的序列化器缓存,否则增加的过滤器不一定能生效 */
DefaultSerializerProvider prov= (DefaultSerializerProvider) objectMapper.getSerializerProvider();
prov.flushCachedSerializers();
}
}
}
7.黑马苍穹外卖
经过以上的学习现在就可以来解读以下苍穹外卖的序列化和反序列化
package com.sky.json;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
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 java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {
//日期格式定制
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
//public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//创建模块并注册日期时间处理器
SimpleModule simpleModule = new SimpleModule()
//反序列化器
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
//序列化器
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
// 注册模块到ObjectMapper
this.registerModule(simpleModule);
}
}
开头为日期格式定制
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
//public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"
忽略未知 JSON 属性:
/收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);配置
FAIL_ON_UNKNOWN_PROPERTIES = false
,确保反序列化时遇到 JSON 中存在但 Java 对象中不存在的属性时不报错。
反序列化时,属性不存在的兼容处理
this.getDeserializationConfig()
. withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
创建模块并注册日期时间处理器
SimpleModule simpleModule = new SimpleModule()
反序列化器
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
反序列化器
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
注册模块到ObjectMapper
this.registerModule(simpleModule);