SpringMVC Json自定义序列化和反序列化的操作方法
作者:黄鹰 发布时间:2022-11-15 13:07:49
需求背景
需求一:SpringMVC构建的微服务系统,数据库对日期的存储是Long类型的时间戳,前端之前是默认使用Long类型时间,现在前端框架改动,要求后端响应数据时,Long类型的时间自动变成标准时间格式(yyyy-MM-dd HH:mm:ss)。
涉及到这个转换的范围挺大,所有的实体表都有创建时间createTime和修改时间updateTime,目前的主要诉求也是针对这两个字段,并且在实体详情数据和列表数据都存在,需要一个统一的方法,对这两个字段进行处理。
需求二:前端请求上传的JSON报文,String类型的内容,可能会出现前后有空格的现象,如果前端框架未对此问题进行处理,后端收到的JSON请求反序列化为对象时,就会出现String类型的值,前后有空格,现需要一个统一的处理方法,对接收的String类型属性执行trim方法。
解决方案
SpringMVC默认的JSON框架为jackson,也可以使用fastjson。
jackson框架
自定义序列化
如果项目使用jackson框架做json序列化,推荐的方案是使用@JsonSerialize注解,示例代码如下:
@JsonSerialize(using = CustomDateSerializer.class)
private Long createTime;
@JsonSerialize(using = CustomDateSerializer.class)
private Long updateTime;
CustomDateSerializer类的实现示例如下:
public class CustomDateSerializer extends JsonSerializer<Long> {
@Override
public void serialize(Long aLong, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(aLong);
jsonGenerator.writeString(sdf.format(date));
}
}
这种方案的好处如下:
自定义的实现类可以复用
精准到需要转换处理的字段,不受限于createTime和updateTime,更贴近于需求
缺点就是需要转换的字段都需要使用注解,工作量有点大
当然有其他的统一处理方案,这里不赘述。
自定义反序列化
在jackson框架上实现自定义序列化,也是非常方便的,继承SimpleModule类即可:
@Component
public class StringTrimModule extends SimpleModule {
public StringTrimModule() {
addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
String value = jsonParser.getValueAsString();
if (StringUtils.isEmpty(value)) {
return value;
}
return value.trim();
}
});
}
}
fastjson框架
如果工程里出现这个依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
说明此工程使用的json框架为fastjson,那么jackson的@JsonSerialize就不会有触发入口了,我们来看看fastjson的处理方式。
自定义序列化
相应的,使用fastjson会有相应的配置类,示例如下:
/**
* 统一输出是采用fastJson
*
* @return
*/
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
//convert转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
//处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
//是否要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
// 添加指定字段的值转换处理
fastJsonConfig.setSerializeFilters(new CustomerDateFilter());
// FastJson禁用autoTypeSupport
fastJsonConfig.getParserConfig().setAutoTypeSupport(false);
fastConverter.setFastJsonConfig(fastJsonConfig);
return new HttpMessageConverters(fastConverter);
}
这里需要添加fastjson对字段值的处理(上述代码已添加这行代码),如
// 添加指定字段的值转换处理
fastJsonConfig.setSerializeFilters(new CustomerDateFilter());
CustomerDateFilter为自行实现的类,代码如下:
public class CustomerDateFilter implements ValueFilter {
@Override
public Object process(Object object, String name, Object value) {
if (FieldConstants.CREATE_TIME.equalsIgnoreCase(name) || FieldConstants.UPDATE_TIME.equalsIgnoreCase(name)) {
// 属性名为createTime, updateTime进行转换处理
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
if(value instanceof Long) {
Long time = (Long) value;
Date date = new Date(time);
return sdf.format(date);
} else {
return value;
}
}
return value;
}
}
这样就可以把所有响应对象中出现的createTime和updateTime字段统一处理了,无论列表数据还是单个对象数据,非常方便。缺点就是除此之外的字段,如果还做不到全系统统一,就需要单独处理。
SerializeFilter定制序列化
支持SerializeFilter定制序列化的扩展编程接口有以下几个,可根据实际需要进行扩展:
PropertyPreFilter: 根据PropertyName判断是否序列化;
PropertyFilter: 根据PropertyName和PropertyValue来判断是否序列化;
NameFilter: 修改Key,如果需要修改Key,process返回值则可;
ValueFilter: 修改Value;
BeforeFilter: 序列化时在最前添加内容;
AfterFilter: 序列化时在最后添加内容;
自定义反序列化
fastJson提供了序列化过滤器,来实现自定义序列化改造,但没有提供反序列化过滤器,来实现对应的功能。
方案:@JSONField注解
回到对JSON报文String类型的值执行trim操作,官网支持@JSONField注解的属性设置(要求fastJson版本1.2.36以上):
@JSONField(format="trim")
private String name;
在JSON报文反序列化时,该实体的name属性会自动执行trim方法进行处理。
此方案只有逐个添加注解,工作量较大。
方案:实现ObjectDeserializer接口
ObjectDeserializer接口为可以实现自定义反序列化实现接口,配合ParserConfig的全局设置,也可以达到预期的效果,合建StringTrimDeserializer类,对String进行处理:
/**
* @title: StringTrimDeserializer
* @description: 把String类型的内容统一做trim操作
*/
public class StringTrimDeserializer implements ObjectDeserializer {
@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
// JSON String反序列化的逻辑比较复杂,在StringCodec的基础上,对其结果调用trim方法
Object obj = StringCodec.instance.deserialze(parser, type, fieldName);
if (obj instanceof String) {
String str = (String) obj;
return (T) str.trim();
}
return (T) obj;
}
@Override
public int getFastMatchToken() {
return JSONToken.LITERAL_STRING;
}
}
相应在,在HttpMessageConverters
类fastJsonHttpMessageConverters方法内中增加String类的反序列化设置:
// 设置String类的全局反序列化规则:自动完成trim操作
ParserConfig.getGlobalInstance().putDeserializer(String.class, new StringTrimDeserializer());
tips:
在StringTrimDeserializer类实现方法中为什么不直接parser.getLexer().stringVal()
得到值后执行trim方法,而是调用StringCodec.instance的实现方法?
StringCodec是fastJson默认的String类型的反序列化逻辑类,里面要处理的类型有String、StringBuffer、StringBuilder等,还有各种的集合、数组结构,涉及的nextToken值都不相同,总之,对String文本的反序列化,实现逻辑和应对的场景都比较复杂,而此次的需求只是对String执行trim操作,复杂的逻辑还是交给StringCodec来处理,站在StringCodec的基础上,对其结果执行trim方法就可以达到预期目标。
小结
今天这篇是记录Json自定义序列化和反序列化的实践方案,开始实施前先确认工程里使用的框架是哪个,否则就会出现添加了@JsonSerialize注解,搞了大半天没有效果,回头一看框架是fastjson,没有触发入口,当然得不到预期效果,小小建议,希望对你有帮助。
来源:https://segmentfault.com/a/1190000039111807


猜你喜欢
- @ConfigurationProperties作用:用于获取配置文件中的属性定义并绑定到javaBean属性中举个栗子:配置文件mycar
- Random类介绍Random类一个用于产生 伪随机 数字的类。这里的伪随机表示有随机性但是可以基于算法模拟出随机规律。Random类的构造
- 一、单例模式设计模式:软件设计模式是一套被反复使用、多数人知晓、经过分类编目、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易
- 在mybatis中sql是写在xml映射文件中的,如果sql中有一些特殊字符的话,在解析xml文件的时候就会被转义,如若不希望被转义,那该怎
- 举一个生活中的小例子,大凡开过学或者毕过业的都会体会到这样一种郁闷:你要去 n个地方办理 n 个手续(现在大学合并后就更加麻烦,因为可能那
- 这篇文章主要介绍了Spring ApplicationListener * 用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具
- 最近做的一个小东西遇到这样的情况,我从一个页面MainActivity修改一些内容,需要跳转到一个新的EditActivity去做修改操作,
- 在app中图片的轮播显示可以说是非常常见的实现效果了,其实现原理不过是利用ViewPager,然后利用handler每隔一定的时间将View
- 本文实例讲述了Android编程实现的一键锁屏程序。分享给大家供大家参考,具体如下:据笔者了解,所有的Android手机都用电源键来手动锁屏
- 一、首先将网页内容整个抓取下来,数据放在byte[]中(网络上传输时形式是byte),进一步转化为String,以便于对其操作,实例如下:p
- android多线程断点下载,带进度条和百分比显示,断点下载的临时数据保存到SD卡的文本文档中,建议可以保存到本地数据库中,这样可以提高存取
- 前言需求是上传Excel文件并读取Excel文件中的内容,根据获取的数据执行完某些业务操作后再将一些数据写回到excel中。前台使用Form
- 1、用ASCII码判断在 ASCII码表中,英文的范围是0-127,而汉字则是大于127,具体代码如下:string text = &quo
- 目录Jacoco原理简介使用Jacoco生成代码执行覆盖率报告小结Jacoco是Java Code Coverage的缩写,顾名思义,它是获
- 今天写这篇文章的缘由是前一段时间一个网友在我的博客上面留言,想要实现在GridLayout(相当于九宫格)中点击每项可左右滑动显
- 《IDEA 破解新招 - 无限重置30天试用期》,这种方法适用idea2021以下所有版本,亲测idea2020.3成功激活,其他版本自测吧
- 需求有一个固定URL的文件,服务器端程序会定期的更新这个文件,现在需要写一个工具来监控这个文件的变化。解决办法最初我想到的是把这个文件下载下
- 1. 前言什么是特殊矩阵?C++,一般使用二维数组存储矩阵数据。在实际存储时,会发现矩阵中有许多值相同的数据或有许多零数据,且分布呈现出一定
- 介绍该系统有三个角色,分别是:普通用户、房屋中介、管理员。普通用户的功能:浏览房屋信息、预约看房、和中介聊天、申请成为中介等等。房屋中介的功
- 阅读收获理解SpringBoot自动配置原理一、SpringBoot是什么SpringBoot 的诞生就是为了简化 Spring 中繁琐的