美文网首页java
关于Spring MVC 返回消息的序列化问题

关于Spring MVC 返回消息的序列化问题

作者: 木巽 | 来源:发表于2023-03-21 17:37 被阅读0次

Spring MVC 默认的序列化工具是Jackson,但是项目中遇到两个问题:

问题1:值为null的字段默认会被序列化

这样的话返回给前端的数据就会包括实体类的全部字段,有些字段在mybatis的查询中故意没写的字段,还是会生成字段名:null 这样的值在JSON字符串中,浪费网络带宽。

问题2:LocalDateTime类型的日期序列化后空格变成“T”字母

一般如果是查询结果映射到实体类,实体类的日期类型为java.sql.Date,字段上又有@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")这样的注解是不会出问题的。但如果查询的结果类型直接是hashmap,不做映射,序列化之后就有问题。比如日期输出的值为:2023-02-13T12:18:11

解决方法一,继续用Jackson

针对问题1,在spring boot的配置文件中加一个配置项就可以了,如下:

spring.jackson.default-property-inclusion = non_null

针对问题2,要新建一个配置类,如下:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
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 org.codehaus.jackson.annotate.JsonValue;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
 
@Configuration
public class LocalDateTimeSerializerConfig {
    @Bean
    public LocalDateTimeSerializer localDateTimeDeserializer() {
        return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
    
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeDeserializer());
    }

或者在SQL语句中,将日期类型的字段格式化为字符型字段。

解决方法二,改用FastJson

使用FastJson代替Spring默认的Jackson,上面的问题就不会出现了。网上有人说FastJson虽然速度快,使用方便,但是安全性没那么好,代码注释少,所以你也可以考虑一下是否采用这种方法。

我这里使用的是fastjson2,然后spring MVC 是6.0的配置,maven引入类库如下:

        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.26</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2-extension-spring6</artifactId>
            <version>2.0.26</version>
        </dependency>

新建一个配置类,我项目中有一个MVC全局拦截器,所以就把代码加到拦截器中了,都是实现了WebMvcConfigurer 接口,代码如下:

package com.ezlcp.commons.config;

import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.support.config.FastJsonConfig;
import com.alibaba.fastjson2.support.spring6.http.converter.FastJsonHttpMessageConverter;
//import com.ezlcp.commons.filter.WebContextFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    /***
     * @description: 添加Spring MVC 拦截器
     * @author Elwin ZHANG
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 先注释掉,可能你没有到拦截器
        //registry.addInterceptor(new WebContextFilter()).addPathPatterns("/**");
    }

    //保证StringHttpMessageConverter在FastJsonHttpMessageConverter前被调用
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.clear();
        StringHttpMessageConverter converter = new StringHttpMessageConverter(
                Charset.forName("UTF-8"));
        converters.add(converter);
        converters.add(getFastJsonHttpMessageConverter());
    }

  //构造 FastJson消息序列化
    public FastJsonHttpMessageConverter getFastJsonHttpMessageConverter() {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        FastJsonConfig config = new FastJsonConfig();
        //WriteMapNullValue——–是否输出值为null的字段,默认为false
        //WriteNullNumberAsZero—-数值字段如果为null,输出为0,而非null
        //WriteNullListAsEmpty—–List字段如果为null,输出为[],而非null
        //WriteNullStringAsEmpty—字符类型字段如果为null,输出为”“,而非null
       //这是spring MVC 6.X的写法(将序列化和反序列的配置分开了)
       //spring MVC 5.X的写法网上很多可以自己搜索一下,或看这个 https://www.jianshu.com/p/65e210623210
        config.setWriterFeatures(
                JSONWriter.Feature.WriteNullListAsEmpty,
                JSONWriter.Feature.WriteNullBooleanAsFalse,
                JSONWriter.Feature.WriteNullNumberAsZero,
                JSONWriter.Feature.BrowserCompatible,
                JSONWriter.Feature.BrowserSecure
        );
        config.setReaderFeatures(
                JSONReader.Feature.AllowUnQuotedFieldNames,
                JSONReader.Feature.NonZeroNumberCastToBooleanAsTrue,
                JSONReader.Feature.TrimString
        );
        converter.setFastJsonConfig(config);
        List<MediaType> mediaTypes = new ArrayList<>(8);
        mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        mediaTypes.add(MediaType.MULTIPART_FORM_DATA);
        mediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
        mediaTypes.add(MediaType.APPLICATION_JSON);
        mediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
        converter.setSupportedMediaTypes(mediaTypes);
        return converter;
    }
}

相关文章

网友评论

    本文标题:关于Spring MVC 返回消息的序列化问题

    本文链接:https://www.haomeiwen.com/subject/bnmmrdtx.html