关于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 框架原有的了哦~。
测试
启动项目,进行测试: