Jackson自动将null对象转为默认值,将Js处理不了大数字转为字符串

Jackson自动将null对象转为默认值,将Js处理不了大数字转为字符串


前言

SpringBoot开发中,接口返回值中将null对象转成对应的默认值。例如String转为空字符串,Integer转为0
SpringBoot项目中,默认使用的是Jackson来进行json序列化,本文对json进行属性配置来完成该功能。另外会讲两个主要功能封装成配置类,可以在yml文件中进行开启或关闭。


一、开发环境准备

准备一个SpringBoot项目
pom.xml文件

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

二、操作步骤

创建自定义的各种类型空对象时的处理器。例如:String的null对象处理器,在处理器中将空null的String对象转为空字符串串

package com.xue.demos.config.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;


public class CustomizeNullJsonSerializer {
    /**
     * 处理数组集合类型的null值
     */
    public static class NullArrayJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeStartArray();
            jsonGenerator.writeEndArray();
        }
    }

    /**
     * 处理字符串类型的null值
     */
    public static class NullStringJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString("");
        }
    }

    /**
     * 处理数值类型的null值
     */
    public static class NullNumberJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeNumber(0);
        }
    }

    /**
     * 处理boolean类型的null值
     */
    public static class NullBooleanJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeBoolean(false);
        }
    }
    /**
     * 处理date类型的null值
     */
    public static class NullDateJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString("");
        }
    }


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

}

在Js中可处理的数据范围是-9007199254740992 ~ 9007199254740992 。但在Java中数字长度或是超过这个限制,Js会将超出的部分填充为0。故需要自定义一个大数字处理器,来解决这个问题。

package com.xue.demos.config.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;

import java.io.IOException;


public class BigNumberSerializer extends NumberSerializer {
    private static final long JS_NUM_MAX = 9007199254740992L;
    private static final long JS_NUM_MIN = -9007199254740992L;
    public static final BigNumberSerializer instance = new BigNumberSerializer(Number.class);

    public BigNumberSerializer(Class<? extends Number> rawType) {
        super(rawType);
    }

    public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        long longValue = value.longValue();
        if (longValue >= JS_NUM_MIN && longValue <= JS_NUM_MAX) {
            super.serialize(value, gen, provider);
        } else {
            gen.writeString(value.toString());
        }

    }
}

自定义修改器,将对应的数据类型和对应的null对象处理器绑定上

package com.xue.demos.config.jackson;

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;

import java.time.temporal.Temporal;
import java.util.Collection;
import java.util.Date;
import java.util.List;


public class CustomizeBeanSerializerModifier extends BeanSerializerModifier {

    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
                                                     BeanDescription beanDesc,
                                                     List<BeanPropertyWriter> beanProperties) {
        // 循环所有的beanPropertyWriter
        for (BeanPropertyWriter writer : beanProperties) {
            // 判断字段的类型,如果是数组或集合则注册nullSerializer
            if (isArrayType(writer)) {
                // 给writer注册一个自己的nullSerializer
                writer.assignNullSerializer(new CustomizeNullJsonSerializer.NullArrayJsonSerializer());
                continue;
            }
            if (isStringType(writer)) {
                writer.assignNullSerializer(new CustomizeNullJsonSerializer.NullStringJsonSerializer());
                continue;
            }
            if (isNumberType(writer)){
                writer.assignNullSerializer(new CustomizeNullJsonSerializer.NullNumberJsonSerializer());
                continue;
            }
            if (isDateType(writer)){
                writer.assignNullSerializer(new CustomizeNullJsonSerializer.NullDateJsonSerializer());
                continue;
            }
            if (isBooleanType(writer)){
                writer.assignNullSerializer(new CustomizeNullJsonSerializer.NullBooleanJsonSerializer());
            }
        }
        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);
    }

    /**
     * 是否是数值类型
     */
    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);
    }
    /**
     * 是否是date
     */
    private boolean isDateType(BeanPropertyWriter writer) {
        Class<?> clazz = writer.getType().getRawClass();
        return clazz.equals(Date.class) || Temporal.class.isAssignableFrom(clazz);
    }

}


为了方便使用,配置config类,并通过ConditionalOnProperty实现通过ymal里面的配置去动态开启或关闭空对象处理功能以及大数字转字符串功能。额外扩充个知识点:在Java中Integer.class来表示Integer类,用Integer.Type来表示int
第一个是配置类:

package com.xue.demos.config.jackson;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.math.BigDecimal;
import java.math.BigInteger;


@Configuration
public class JacksonConfig {
    @Bean
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    @ConditionalOnProperty(name = "nullToEmpty",prefix = "jackson.config", havingValue = "true")
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        /** 为objectMapper注册一个带有SerializerModifier的Factory */
        objectMapper.setSerializerFactory(objectMapper.getSerializerFactory()
                .withSerializerModifier(new CustomizeBeanSerializerModifier()));

        SerializerProvider serializerProvider = objectMapper.getSerializerProvider();
        serializerProvider.setNullValueSerializer(new CustomizeNullJsonSerializer
                .NullObjectJsonSerializer());
        return objectMapper;
    }

    @Bean
    @ConditionalOnMissingBean(Jackson2ObjectMapperBuilderCustomizer.class)
    @ConditionalOnProperty(name = "bigNumToString",prefix = "jackson.config", havingValue = "true")
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            //将Long类型转换成string类型返回,避免大整数导致前端精度丢失的问题
            builder.serializerByType(Long.TYPE, BigNumberSerializer.instance);
            builder.serializerByType(Long.class, BigNumberSerializer.instance);
            builder.serializerByType(BigInteger.class, BigNumberSerializer.instance);
            builder.serializerByType(BigDecimal.class, ToStringSerializer.instance);
        };
    }
}

第二个是用于yaml中的控制类,通过nullToEmpty和bigNumToString两个属性来控制功能是否开户。两个值的默认是true,也就是说默认开启

package com.xue.demos.config.jackson;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


@Component
@ConfigurationProperties(prefix = "jackson.config")
public class JacksonProperties {
    private Boolean nullToEmpty;
    private Boolean bigNumToString;

    public JacksonProperties() {
        nullToEmpty = Boolean.TRUE;
        bigNumToString = Boolean.TRUE;
    }

    public Boolean getNullToEmpty() {
        return nullToEmpty;
    }

    public void setNullToEmpty(Boolean nullToEmpty) {
        this.nullToEmpty = nullToEmpty;
    }

    public Boolean getBigNumToString() {
        return bigNumToString;
    }

    public void setBigNumToString(Boolean bigNumToString) {
        this.bigNumToString = bigNumToString;
    }
}

下面是yaml配置

# 应用服务 WEB 访问端口
server:
  port: 8080
spring:
  application:
    name: jackson-demo
jackson:
  config:
    nullToEmpty: true # 空对象转换
    bigNumToString: true # 大数字转换字符串

测试阶段

我们建议一个实体类,以及一个接口,通过接口的返回值来查看功能是否生效。
对应实体类,在实体类使用lombok注解来简化代码:

package com.xue.demos.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class TestJson {
    private Integer userId;
    private String userName;
    // 大数字的long对象
    private Long bigNumLong = 9007199254740999L;
    private Long smallNumLong = 1000L;
    private Double userIdDouble;
    private Date date;
    private LocalDate date1;
    private List list;
    private Map map;
}

controller层的接口,不做其他过多的操作,只是new 一个实体类,然后返回,看下接口的返回值

package com.xue.demos.controller;

import com.xue.demos.entity.TestJson;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestJsonController {

    @GetMapping("/testJackson")
    public TestJson testJackson() {
        return new TestJson();
    }
}

接口返回值
通过截图可以发现,空对象已经转换为对应的默认值。例如:Integer的userId转为了0。
而大数字的bigNumLong 也已经转为了字符串,而小数字smallNumLong则没变。

总结

以上就是今天要讲的内容,本文仅仅简单介绍了Jackson在SpringBoot中的一个使用,而这个使用也可以方便前端对象的一个处理和使用。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值