SpringMVC Json自定义序列化和反序列化的操作方法

 更新时间:2021年01月29日 10:08:57   作者:黄鹰  
这篇文章主要介绍了SpringMVC Json自定义序列化和反序列化的操作方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

需求背景

需求一: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,没有触发入口,当然得不到预期效果,小小建议,希望对你有帮助。

到此这篇关于SpringMVC Json自定义序列化和反序列化的文章就介绍到这了,更多相关SpringMVC 序列化和反序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java超细致讲解数组的使用

    Java超细致讲解数组的使用

    数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同。Java 语言中提供的数组是用来存储固定大小的同类型元素
    2022-05-05
  • Spring实战之搜索Bean类操作示例

    Spring实战之搜索Bean类操作示例

    这篇文章主要介绍了Spring实战之搜索Bean类操作,结合实例形式分析了Spring搜索Bean类的相关配置、接口实现与操作技巧,需要的朋友可以参考下
    2019-12-12
  • Java读取网页内容并下载图片的实例

    Java读取网页内容并下载图片的实例

    这篇文章主要介绍了Java读取网页内容并下载图片的实例的相关资料,希望通过本文能帮助到大家,让大家实现这样的功能,需要的朋友可以参考下
    2017-09-09
  • Java中char[]输出不是内存地址的原因详解

    Java中char[]输出不是内存地址的原因详解

    这篇文章主要介绍了关于Java中char[]输出为什么不是内存地址的原因,文中通过示例代码介绍的很详细,需要的朋友们可以参考学习。
    2017-03-03
  • spring boot 利用注解实现权限验证的实现代码

    spring boot 利用注解实现权限验证的实现代码

    这篇文章主要介绍了spring boot 利用注解实现权限验证的实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • java 多线程的几种实现方法总结

    java 多线程的几种实现方法总结

    这篇文章主要介绍了java 多线程的几种实现方法总结的相关资料,希望通过本文能帮助到大家,让大家掌握java多线程的知识,需要的朋友可以参考下
    2017-10-10
  • Jenkins Pipeline 部署 SpringBoot 应用的教程详解

    Jenkins Pipeline 部署 SpringBoot 应用的教程详解

    这篇文章主要介绍了Jenkins Pipeline 部署 SpringBoot 应用的详细教程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • 详解将Eclipse代码导入到AndroidStudio的两种方式

    详解将Eclipse代码导入到AndroidStudio的两种方式

    本篇文章主要介绍了详解将Eclipse代码导入到AndroidStudio的两种方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • Jmeter访问需要登录的接口如何处理问题

    Jmeter访问需要登录的接口如何处理问题

    这篇文章主要介绍了Jmeter访问需要登录的接口如何处理问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • Java 中限制方法的返回时间最新方法

    Java 中限制方法的返回时间最新方法

    最近在研究 ChatGPT 的 API 调用,因为 ChatGPT 的 API 调用时间通常超过 30 秒,所以我们希望在程序中限制这个方法的执行时间,不要让方法花太长时间去执行了,今天通过本文给大家分享Java 中如何限制方法的返回时间,感兴趣的朋友跟随小编一起看看吧
    2023-05-05

最新评论