在SpringBoot自动配置的ObjectMappe基础上增加对空值处理,null转空串"",List、Array转[],Int转0

摘要:
/***正在处理数组集合类型*/publicstaticclassNullArrayJsonSerializerextendsJsonSerializeder<}}}/***的空值正在处理字符串类型*/publicstaticclassNullStringJsonSerializeextendsJsonSerializeder<的空值;

在SpringBoot自动配置的ObjectMappe基础上增加对空值处理,null转空串"",List、Array转[],Int转0;同时保证SpringBoot自动加载的配置不丢失;网上的一些教程照着改后都是把默认的ObjectMapper配置搞丢,导致我之前配置时间格式,Long精度都时效了,故通过分析产生以下处理方式:

1.自定义Null序列器;我这里主要用了两个,其他的先写着,有需要是再调整

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;

/**
 * @Author:LJ
 * @Description:自定义Jackson对数组、字符串、数值、布尔和实体为null的序列化;
 * @Date: 2020/11/25
 * @Modified By:
 */
public class CustomizeNullJsonComponent {
    public final static JsonSerializer<Object> NULL_ARRAY_SERIALIZER=new CustomizeNullJsonComponent.NullArrayJsonSerializer();

    public final static JsonSerializer<Object> NULL_STRING_SERIALIZER=new CustomizeNullJsonComponent.NullStringJsonSerializer();

    /**
     * 处理数组集合类型的null值
     */
    public static class NullArrayJsonSerializer extends JsonSerializer<Object> {

        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
            if (value == null) {
                gen.writeStartArray();
                gen.writeEndArray();
            } else {
                gen.writeObject(value);
            }
        }
    }

    /**
     * 处理字符串类型的null值
     */
    public static class NullStringJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
            if (value == null) {
                gen.writeString(StringUtils.EMPTY);
            } else {
                gen.writeObject(value);
            }
        }
    }

    /**
     * 处理数值类型的null值
     */
    public static class NullNumberJsonSerializer extends JsonSerializer<Object> {

        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
            if (value == null) {
                gen.writeNumber(0);
            } else {
                gen.writeObject(value);
            }
        }
    }

    /**
     * 处理boolean类型的null值
     */
    public static class NullBooleanJsonSerializer extends JsonSerializer<Object> {

        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
            if (value == null) {
                gen.writeBoolean(false);
            } else {
                gen.writeObject(value);
            }
        }
    }

    /**
     * 处理实体对象类型的null值
     */
    public static class NullObjectJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            if (value == null) {
                gen.writeStartObject();
                gen.writeEndObject();
            } else {
                gen.writeObject(value);
            }
        }
    }
}

2.自定义DefaultSerializerProvider

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.util.Collection;
import java.util.List;

/**
 * @Author:LJ
 * @Description: <div>
 * 该类的作用是使用SpringBoot自动配置的Jackson ObjectMapper支持将null的数组转为[]、null字符串转为""
 * </div>
 * <p>
 * 1.发现{@link SerializerProvider#findNullValueSerializer(BeanProperty)}用于序列化属性值为null;
 * 而该接口的默认实现是通过{@link SerializerProvider#getDefaultNullValueSerializer()}获取的{@link SerializerProvider#_nullValueSerializer};
 * 默认初始为{@link com.fasterxml.jackson.databind.ser.std.NullSerializer#instance}
 * <p>
 * 2.因为{@link SerializerProvider}为抽象类;
 * -->{@link DefaultSerializerProvider}也为抽象类,并且所有自定义的{@link SerializerProvider}都必须继承此类;因为{@link ObjectMapper}需要这个类型
 * -->默认实现类{@link DefaultSerializerProvider.Impl},该类在{@link ObjectMapper#ObjectMapper(JsonFactory, DefaultSerializerProvider, DefaultDeserializationContext)}的
 * 构造函数中有如下一行代码:
 * <code>
 * _serializerProvider = (sp == null) ? new DefaultSerializerProvider.Impl() : sp;
 * </code>
 * 我们可以看到,在没有指定DefaultSerializerProvider的时候,默认实例化DefaultSerializerProvider.Impl()
 * <br>
 * 3.下面我们就来理一下SpringBoot是怎么实例化ObjectMapper
 * 首先定位{@link org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration}配置类
 * -->在{@link org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.JacksonObjectMapperBuilderConfiguration#jacksonObjectMapperBuilder(List)}方法上发现了
 * <code>
 * @ConditionalOnMissingBean(Jackson2ObjectMapperBuilder.class) </code>
 * 也就是在没有Jackson2ObjectMapperBuilder时会执行jacksonObjectMapperBuilder(List)方法;
 * 而JacksonAutoConfiguration配置类中的其他内容都是
 * <code>
 * @ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
 * </code>
 * 需要依托Jackson2ObjectMapperBuilder和ObjectMapper
 * 4.通过第3步我们定位到Jackson2ObjectMapperBuilder实在JacksonAutoConfiguration.JacksonObjectMapperBuilderConfiguration#jacksonObjectMapperBuilder(List)中实例的,那么ObjectMapper呢?
 * -->在{@link org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.JacksonObjectMapperConfiguration#jacksonObjectMapper(Jackson2ObjectMapperBuilder)}中实例化的
 * 该方法被
 * <code>
 * @ConditionalOnMissingBean(ObjectMapper.class) </code>
 * 注解修饰,并且传递Jackson2ObjectMapperBuilder参数,通过{@link Jackson2ObjectMapperBuilder#build()}构造ObjectMapper
 * -->进而调整用{@link ObjectMapper#ObjectMapper()}
 * -->{@link ObjectMapper#ObjectMapper(JsonFactory, DefaultSerializerProvider, DefaultDeserializationContext)}
 * 在此处就对接到了第2步中<pre>_serializerProvider = (sp == null) ? new DefaultSerializerProvider.Impl() : sp;</pre>
 *
 * 总结:
 *  所以本类通过继承{@link DefaultSerializerProvider}实现自定义的null处理转换逻辑
 * </p>
 * @Date: 2020/11/25
 * @Modified By:
 */
public class NullValueSerializerProvider extends DefaultSerializerProvider {

    public NullValueSerializerProvider() {
        super();
    }

    protected NullValueSerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) {
        super(src, config, f);
    }

    @Override
    public DefaultSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) {
        return new NullValueSerializerProvider(this, config, jsf);
    }

    @Override
    public JsonSerializer<Object> findNullValueSerializer(BeanProperty property) throws JsonMappingException {
        if (isStringType(property)) {
            return CustomizeNullJsonComponent.NULL_STRING_SERIALIZER;
        } else if (isArrayType(property)) {
            return CustomizeNullJsonComponent.NULL_ARRAY_SERIALIZER;
        } else {
            return super.findNullValueSerializer(property);
        }
    }

    /**
     * 是否是数组
     */
    private boolean isArrayType(BeanProperty property) {
        Class<?> clazz = property.getType().getRawClass();
        return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
    }

    /**
     * 是否是String
     */
    private boolean isStringType(BeanProperty property) {
        Class<?> clazz = property.getType().getRawClass();
        return CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz);
    }
}

至于这里为啥是重写DefaultSerializerProvider#findNullValueSerializer的方法,因为debug调试链如下:

>org.springframework.http.converter.json.MappingJackson2HttpMessageConverter(Object object, Type type, HttpOutputMessage outputMessage)
>org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal
-->org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
    方法中的 objectWriter.writeValue(generator, value);
---->com.fasterxml.jackson.databind.ObjectWriter#writeValue(com.fasterxml.jackson.core.JsonGenerator, java.lang.Object)
        方法中的 _prefetch.serialize(gen, value, _serializerProvider());
------>com.fasterxml.jackson.databind.ObjectWriter.Prefetch#serialize(JsonGenerator gen, Object value, DefaultSerializerProvider prov)
            方法中的 prov.serializeValue(gen, value);
-------->com.fasterxml.jackson.databind.ser.DefaultSerializerProvider#serializeValue(com.fasterxml.jackson.core.JsonGenerator, java.lang.Object)
                方法中的 _serializeNull(gen);
---------->com.fasterxml.jackson.databind.ser.DefaultSerializerProvider#_serializeNull(JsonGenerator gen)
                    方法中的 JsonSerializer<Object> ser = getDefaultNullValueSerializer();
                    
------------>com.fasterxml.jackson.databind.SerializerProvider#getDefaultNullValueSerializer
                    跟踪到这里后发现与com.fasterxml.jackson.databind.SerializerProvider#_nullValueSerializer有关
                    所以,查找该值是否有set方法,定位到com.fasterxml.jackson.databind.SerializerProvider#setNullValueSerializer
            该方法注释中提到可以通过重写{@link #findNullValueSerializer}来更好的序列化控制,因为该方法在bean属性进行细粒度控制

3.自定义Jackson2ObjectMapperBuilder

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

/**
 * @Author:LJ
 * @Description: <p>
 * 本类主要目的是通过继承{@link Jackson2ObjectMapperBuilder}来重写{@link Jackson2ObjectMapperBuilder#configure(ObjectMapper)}方法
 * 来达到给ObjectMapper设置自定义DefaultSerializerProvider的目的
 * </p>
 * @Date: 2020/11/25
 * @Modified By:
 */
public class DecorateJackson2ObjectMapperBuilder extends Jackson2ObjectMapperBuilder {

    @Override
    public void configure(ObjectMapper objectMapper) {
        super.configure(objectMapper);
        /**
         * 给ObjectMapper设置自定义的DefaultSerializerProvider
         * {@link NullValueSerializerProvider}
         */
        objectMapper.setSerializerProvider(new NullValueSerializerProvider());
    }
}

4.替换SpringBoot自动生成的Jackson2ObjectMapperBuilder为咱们自定义的构造器

import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.vrv.app.ecs.config.jackson.DecorateJackson2ObjectMapperBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.util.List;

/**
 * @Author:LJ
 * @Description:
 * @Date: 2020/8/25
 * @Modified By:
 */
@Configuration
public class SpringBootConfig  {

    /***Jackson配置***/
    @Autowired
    private ApplicationContext applicationContext;

    /**
     * @author LJ
     * @description <p>
     * 此方法逻辑复制于{@link org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.JacksonObjectMapperBuilderConfiguration#jacksonObjectMapperBuilder(List)};
     * 处于简洁,将原来逻辑做了合并和替换
     * 主要目的就是用自定义的{@link DecorateJackson2ObjectMapperBuilder}替换SpringBoot默认的{@link Jackson2ObjectMapperBuilder}
     * </p>
     * @date 2020/11/26 11:01
     */
    @Bean
    public Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder(List<Jackson2ObjectMapperBuilderCustomizer> customizers) {
        // 用自定义的DecorateJackson2ObjectMapperBuilder替换默认的Jackson2ObjectMapperBuilder
        Jackson2ObjectMapperBuilder builder = new DecorateJackson2ObjectMapperBuilder();
        builder.applicationContext(this.applicationContext);
        for (Jackson2ObjectMapperBuilderCustomizer customizer : customizers) {
            customizer.customize(builder);
        }
        return builder;
    }

    /**
     * 通过定义jackson2ObjectMapperBuilderCustomizer,对Jackson2ObjectMapper对象
     * 进行定制,然后对Long型数据使用ToStringSerializer进行序列化
     * 解决的Long数据在JS中精度丢失的问题
     *
     * @return
     */
    @Bean("jackson2ObjectMapperBuilderCustomizer")
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        Jackson2ObjectMapperBuilderCustomizer customizer = jacksonObjectMapperBuilder ->
                jacksonObjectMapperBuilder
                        .serializerByType(Long.class, ToStringSerializer.instance);
        /**
         * LJ 2020/9/2 14:04 TODO 这里将long类型排除,
         * 原因返回给{@link com.baomidou.mybatisplus.plugins.pagination.Pagination}
         * 的page和total因为long类型,被序列化为字符串化后前端之前的分页组件都需要进行调整改动太大;
         * 故,排除long;
         * 所以,之后的开发请在需要long类的地方改写为Long类型
         */
        //.serializerByType(Long.TYPE, ToStringSerializer.instance)
        return customizer;
    }

}

至此,咱们在保留SpringBoot自动初始的ObjectMapper的配置基础上,调整了ObjectMapper默认的null处理方式

参考:

1.SpringBoot使用Jackson对空值处理,null转空串"",List、Array转[],Int转0

2.jackson serializer cover String null to empty String(“”) and keep object null is null

3.【私人定制jackson】定制jackson的自定义序列化(null值的处理)

免责声明:文章转载自《在SpringBoot自动配置的ObjectMappe基础上增加对空值处理,null转空串"",List、Array转[],Int转0》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇gdb常用命令three.js使用gpu选取物体并计算交点位置下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Spring 注解(二)注解工具类

本文转载自Spring 注解(二)注解工具类 导语 首先回顾一下 AnnotationUtils 和 AnnotatedElementUtils 这两个注解工具类的用法: @Test @GetMapping(value = "/GetMapping", consumes = MediaType.APPLICATION_JSON_VALUE) public...

easyPoi 工具类

import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.ExcelImportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; import cn.afterturn.ea...

HSF服务的开发与使用

转载:http://www.cnblogs.com/cloudml/p/4675705.html#undefined 1.HSF服务的开发 1) 基于Maven创建一个web工程HSFService,如下图,其他的可以自定义。 2)创建好好在src/main目录下创建一个java目录,并将其设置为sources folder,如下图所示: 3) 配置项...

WebSocket-java实现

一、所需jar 二、创建websocket.jsp(注意此页面链接,需要链接到自己的服务) 1 <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> 2 <% 3 String path = request.getContextPath...

很酷的页面布局框架

建高楼大厦时,我们总是先用钢筋水泥搭好骨架,然后再加砖添瓦,布置每套房屋。编写一个网页也是这样,须先将布局框架搭好稳固,之后才能游刃有余。本博文阐述如何实现了两栏布局(main栏和sub栏),三栏布局(main栏、sub栏和extra栏),以及布局中各栏的排列顺序。 本示例布局框架具有以下特性: 跨浏览器:兼容IE6。 稳固性:布局稳固,不受内容的影响。...

rbac之 权限粒度控制到按钮级别

rbac之 权限粒度控制到按钮级别:  这里的意思就是 如果当前用户,没有这个权限。 那么这个相对应的这个按钮的权限, 就不应该展示。看都不能给看到。 思路:  为每一个权限,设置一个别名。  这里是这的别名。 要与 路由控制器中的,每条路径的 别名保持一直  模板中每一个按钮标签的位置,进行 if 判断。 判断这个别名是否在当前用户的,权限字典中。 如果...