关于Blade框架对数字类型的null值转为-1问题

关于Blade框架对数字类型的null值转为-1问题

问题:最近项目遇到了一个问题,SpringBoot项目后端Long类型的数据,如果是NULL的话,返回给前端的时候是-1。如下图:

分析

是由于使用了 blade 框架,项目启动的时候会执行MessageConfiguration类的代码:

MappingApiJackson2HttpMessageConverter这个类会执行:

BladeBeanSerializerModifier类对于 Number 类型会执行下面:


可以看到设置为 -1了。

解决办法

从上面我们可以看出主要是由于BladeBeanSerializerModifier类里面的方法,如果 Number 类型就会 NUMBER_JSON_SERIALIZER ,这个里面设为了 -1,那么我们只需要修改这里为 我们想返回的值就好。

但是这是源码,我们不能直接修改,所以就要"围魏救赵"。我们可以采用下面的方法

1、添加三个类:

  • MessageConfiguration :
java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springblade.core.tool.jackson.BladeJacksonProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
@Order(value = (Integer.MIN_VALUE + 1))
public class MessageConfiguration implements WebMvcConfigurer {

	private final ObjectMapper objectMapper;
	private final BladeJacksonProperties properties;

	public MessageConfiguration(final ObjectMapper objectMapper, final BladeJacksonProperties properties) {
		this.objectMapper = objectMapper;
		this.properties = properties;
	}

	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		converters.removeIf((x) -> {
			return x instanceof AbstractJackson2HttpMessageConverter;
		});
		converters.add(new MappingApiJackson2HttpMessageConverter(this.objectMapper, this.properties));
	}
}

注意:Order 设置为 Integer.MIN_VALUE + 1,让我们写的这个类的 configureMessageConverters 方法,执行顺序在 原本MessageConfiguration类的configureMessageConverters方法之后,在我们的方法移出他们框架的MappingApiJackson2HttpMessageConverter,添加我们自己的 MappingApiJackson2HttpMessageConverter 类(后面代码有提供)。

  • MappingApiJackson2HttpMessageConverter :
java 复制代码
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.springblade.core.tool.jackson.AbstractReadWriteJackson2HttpMessageConverter;
import org.springblade.core.tool.jackson.BladeJacksonProperties;
import org.springblade.core.tool.jackson.BladeNumberModule;
import org.springblade.core.tool.utils.Charsets;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.StreamUtils;

public class MappingApiJackson2HttpMessageConverter extends AbstractReadWriteJackson2HttpMessageConverter {
    @Nullable
    private String jsonPrefix;

    public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper, BladeJacksonProperties properties) {
        super(objectMapper, initWriteObjectMapper(objectMapper, properties), initMediaType(properties));
    }

    private static List<MediaType> initMediaType(BladeJacksonProperties properties) {
        List<MediaType> supportedMediaTypes = new ArrayList();
        supportedMediaTypes.add(MediaType.APPLICATION_JSON);
        supportedMediaTypes.add(new MediaType("application", "*+json"));
        if (Boolean.TRUE.equals(properties.getSupportTextPlain())) {
            supportedMediaTypes.add(MediaType.TEXT_PLAIN);
        }

        return supportedMediaTypes;
    }

    private static ObjectMapper initWriteObjectMapper(ObjectMapper readObjectMapper, BladeJacksonProperties properties) {
        ObjectMapper writeObjectMapper = readObjectMapper.copy();
        if (Boolean.TRUE.equals(properties.getBigNumToString())) {
            writeObjectMapper.registerModules(new Module[]{BladeNumberModule.INSTANCE});
        }

        if (Boolean.TRUE.equals(properties.getNullToEmpty())) {
            writeObjectMapper.setSerializerFactory(writeObjectMapper.getSerializerFactory().withSerializerModifier(new BladeBeanSerializerModifier()));
            writeObjectMapper.getSerializerProvider().setNullValueSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
        }

        return writeObjectMapper;
    }

    protected void writeInternal(@NonNull Object object, @Nullable Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        if (object instanceof String) {
            Charset defaultCharset = this.getDefaultCharset();
            Charset charset = defaultCharset == null ? Charsets.UTF_8 : defaultCharset;
            StreamUtils.copy((String)object, charset, outputMessage.getBody());
        } else {
            super.writeInternal(object, type, outputMessage);
        }

    }

    public void setJsonPrefix(@Nullable String jsonPrefix) {
        this.jsonPrefix = jsonPrefix;
    }

    public void setPrefixJson(boolean prefixJson) {
        this.jsonPrefix = prefixJson ? ")]}', " : null;
    }

    protected void writePrefix(@NonNull JsonGenerator generator, @NonNull Object object) throws IOException {
        if (this.jsonPrefix != null) {
            generator.writeRaw(this.jsonPrefix);
        }

    }
}
  • BladeBeanSerializerModifier:
java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
/

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;

import java.io.IOException;
import java.time.OffsetDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Collection;
import java.util.Date;
import java.util.List;

public class BladeBeanSerializerModifier extends BeanSerializerModifier {
    public BladeBeanSerializerModifier() {
    }

    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
        beanProperties.forEach((writer) -> {
            if (!writer.hasNullSerializer()) {
                JavaType type = writer.getType();
                Class<?> clazz = type.getRawClass();
				if (type.isTypeOrSubTypeOf(Number.class)) {
					writer.assignNullSerializer(BladeBeanSerializerModifier.NullJsonSerializers.NUMBER_JSON_SERIALIZER);
				} else if (type.isTypeOrSubTypeOf(Boolean.class)) {
					writer.assignNullSerializer(BladeBeanSerializerModifier.NullJsonSerializers.BOOLEAN_JSON_SERIALIZER);
                } else if (type.isTypeOrSubTypeOf(Character.class)) {
                    writer.assignNullSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
                } else if (type.isTypeOrSubTypeOf(String.class)) {
                    writer.assignNullSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
                } else if (!type.isArrayType() && !clazz.isArray() && !type.isTypeOrSubTypeOf(Collection.class)) {
                    if (type.isTypeOrSubTypeOf(OffsetDateTime.class)) {
                        writer.assignNullSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
                    } else if (!type.isTypeOrSubTypeOf(Date.class) && !type.isTypeOrSubTypeOf(TemporalAccessor.class)) {
                        writer.assignNullSerializer(BladeBeanSerializerModifier.NullJsonSerializers.OBJECT_JSON_SERIALIZER);
                    } else {
                        writer.assignNullSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
                    }
                } else {
                    writer.assignNullSerializer(BladeBeanSerializerModifier.NullJsonSerializers.ARRAY_JSON_SERIALIZER);
                }

            }
        });
        return super.changeProperties(config, beanDesc, beanProperties);
    }

    public interface NullJsonSerializers {
        JsonSerializer<Object> STRING_JSON_SERIALIZER = new JsonSerializer<Object>() {
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            
		// 这里是关键:你要是想返回 NULL,这样写:gen.writeNull()
		// 返回空字符串按照下面写:
                gen.writeString("");
            }
        };
        JsonSerializer<Object> NUMBER_JSON_SERIALIZER = new JsonSerializer<Object>() {
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
				gen.writeString("");
            }
        };
        JsonSerializer<Object> BOOLEAN_JSON_SERIALIZER = new JsonSerializer<Object>() {
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeObject(Boolean.FALSE);
            }
        };
        JsonSerializer<Object> ARRAY_JSON_SERIALIZER = new JsonSerializer<Object>() {
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeStartArray();
                gen.writeEndArray();
            }
        };
        JsonSerializer<Object> OBJECT_JSON_SERIALIZER = new JsonSerializer<Object>() {
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeStartObject();
                gen.writeEndObject();
            }
        };
    }
}

注意:上面三个类导入的都是我们重写的类,不要导入 blade 框架原有的了哦~。

测试

启动项目,进行测试:

相关推荐
越来越无动于衷几秒前
代理模式深度解析:从静态代理到 Spring AOP 实现
java·spring·代理模式
Meteors.1 分钟前
23种设计模式——适配器模式(Adapter)详解
java·设计模式·适配器模式
喂完待续12 分钟前
【序列晋升】12 Spring Boot 约定优于配置
java·spring boot·spring·架构·约定大于配置·序列晋升·tech arch
David爱编程1 小时前
Java 守护线程 vs 用户线程:一文彻底讲透区别与应用
java·后端
即将进化成人机2 小时前
Maven架构的依赖管理和项目构建
java·架构·maven
qianmoq2 小时前
第03章:无限流:generate()和iterate()的神奇用法
java
whitepure2 小时前
万字详解JVM
java·jvm·后端
我崽不熬夜2 小时前
Java的条件语句与循环语句:如何高效编写你的程序逻辑?
java·后端·java ee
我崽不熬夜3 小时前
Java中的String、StringBuilder、StringBuffer:究竟该选哪个?
java·后端·java ee
草明3 小时前
docker stats 增加一列容器名称的显示
java·开发语言·docker