在SpringBoot自動配置的ObjectMappe基礎上增加對空值處理,null轉空串"",List、Array轉[],Int轉0


在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值的處理)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM