美文网首页
Fastjson vs Jackson, Jackson配置Nu

Fastjson vs Jackson, Jackson配置Nu

作者: 架构路上的一亩三分地 | 来源:发表于2022-06-20 14:33 被阅读0次

    最近在使用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<Object> {
            @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<Object> {
    
            @Override
            public void serialize(Object o, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
                gen.writeString(StringUtils.EMPTY);
            }
        }
    
        /**
         * 处理数字类型的null值
         */
        public class NullNumberJsonSerializer extends JsonSerializer<Object> {
    
            @Override
            public void serialize(Object o, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
                gen.writeNumber(0);
            }
        }
    
        /**
         * 处理布尔类型的null值
         */
        public class NullBooleanJsonSerializer extends JsonSerializer<Object> {
    
            @Override
            public void serialize(Object o, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
                gen.writeBoolean(false);
            }
        }
    
        public class MyBeanSerializerModifier extends BeanSerializerModifier {
    
            @Override
            public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> 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配置Nu

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