Fastjson vs Jackson, Jackson配置Null时返回空值

最近在使用apifox进行自动化接口测试时,发现当String, Integer等返回Null时,无法通过默认的返回数据结构校验。

image.png

于是开始寻找响应解决方案,满足以下需求:

Boolean字段如果为null,输出为false,而非null
数值字段如果为null,输出为0,而非null
List字段如果为null,输出为[],而非null;
字符类型字段如果为null,输出为"",而非null

基于Fastjson的解决方案相对比较简单,但是Spring默认的Json处理框架是Jackson,如果替换为Fastjson,会导入引入一些额外问题,遇到过的比如有wx的反方授权回调接口参数解析错误,返回的字段外面多套了一层“”。

另外参考了maven repository 上 ,jackson的使用还是大大大于fastjson,且最近fastjson又有爆出安全漏洞,最终还是决定改回Jackson,基于Jackson来解决Null的返回值问题。

image.png

试了几种方案,走了一些弯路,这里直接上最终方案吧。

自定义JacksonConfig类,设置返回所有字段,实现自定义BeanSerializerModifier,注入到SerializerFactory中,判断字段类型如果为列表、字符、数字、Boolean,则指定自定义的Null序列化实现

同时,由于Jackson针对日志时间处理的默认处理格式为yyyy-MM-ddTHH:mm:ss,在配置类中改为yyyy-MM-dd HH:mm:ss格式。

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
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.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.List;

import static cn.hutool.core.date.DatePattern.*;

/**
 * Jackson全局配置
 *
 * @author ArchitectRoad
 * @date 2022/6/20
 */
@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        final ObjectMapper mapper = new ObjectMapper()
                // 反序列化设置 关闭反序列化时Jackson发现无法找到对应的对象字段,便会抛出UnrecognizedPropertyException: Unrecognized field xxx异常
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                // 序列化设置 关闭日志输出为时间戳的设置
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                // 返回所有字段
                .setSerializationInclusion(JsonInclude.Include.ALWAYS);

        mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));

//        mapper.enable(MapperFeature.USE_STD_BEAN_NAMING);
//        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        // DateFormat
        mapper.setDateFormat(new SimpleDateFormat(NORM_DATETIME_PATTERN));
        // LocalDate
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(
                LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(NORM_DATE_PATTERN)));
        javaTimeModule.addDeserializer(
                LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(NORM_DATE_PATTERN)));
        // LocalDateTime
        javaTimeModule.addSerializer(
                LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(NORM_DATETIME_PATTERN)));
        javaTimeModule.addDeserializer(
                LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(NORM_DATETIME_PATTERN)));
        // LocalTime
        javaTimeModule.addSerializer(
                LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(NORM_TIME_PATTERN)));
        javaTimeModule.addDeserializer(
                LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(NORM_TIME_PATTERN)));

        // 先注册自定义模块,再注册Java 8相关模块
        mapper.registerModule(javaTimeModule);
        mapper.registerModule(new Jdk8Module());
        return mapper;
    }

    /**
     * 处理数组类型的null值
     */
    public class NullArrayJsonSerializer extends JsonSerializer {
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            if (value == null) {
                gen.writeStartArray();
                gen.writeEndArray();
            }
        }
    }

    /**
     * 处理字符串类型的null值
     */
    public class NullStringJsonSerializer extends JsonSerializer {

        @Override
        public void serialize(Object o, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
            gen.writeString(StringUtils.EMPTY);
        }
    }

    /**
     * 处理数字类型的null值
     */
    public class NullNumberJsonSerializer extends JsonSerializer {

        @Override
        public void serialize(Object o, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
            gen.writeNumber(0);
        }
    }

    /**
     * 处理布尔类型的null值
     */
    public class NullBooleanJsonSerializer extends JsonSerializer {

        @Override
        public void serialize(Object o, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
            gen.writeBoolean(false);
        }
    }

    public class MyBeanSerializerModifier extends BeanSerializerModifier {

        @Override
        public List changeProperties(SerializationConfig config, BeanDescription beanDesc, List beanProperties) {
            //循环所有的beanPropertyWriter
            for (Object beanProperty : beanProperties) {
                BeanPropertyWriter writer = (BeanPropertyWriter) beanProperty;
                //判断字段的类型,如果是array,list,set则注册nullSerializer
                if (isArrayType(writer)) {
                    //给writer注册一个自己的nullSerializer
                    writer.assignNullSerializer(new NullArrayJsonSerializer());
                } else if (isNumberType(writer)) {
                    writer.assignNullSerializer(new NullNumberJsonSerializer());
                } else if (isBooleanType(writer)) {
                    writer.assignNullSerializer(new NullBooleanJsonSerializer());
                } else if (isStringType(writer)) {
                    writer.assignNullSerializer(new NullStringJsonSerializer());
                }
            }
            return beanProperties;
        }

        /**
         * 是否是数组
         */
        private boolean isArrayType(BeanPropertyWriter writer) {
            Class clazz = writer.getType().getRawClass();
            return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
        }

        /**
         * 是否是string
         */
        private boolean isStringType(BeanPropertyWriter writer) {
            Class clazz = writer.getType().getRawClass();
            return CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz);
        }

        /**
         * 是否是int
         */
        private boolean isNumberType(BeanPropertyWriter writer) {
            Class clazz = writer.getType().getRawClass();
            return Number.class.isAssignableFrom(clazz);
        }

        /**
         * 是否是boolean
         */
        private boolean isBooleanType(BeanPropertyWriter writer) {
            Class clazz = writer.getType().getRawClass();
            return clazz.equals(Boolean.class);
        }
    }
}
 
 

如果对你有帮助,请点赞

你可能感兴趣的:(Fastjson vs Jackson, Jackson配置Null时返回空值)