🎉 增强jackson默认配置,支持大数字自动转字符串,以及开启关闭空数字类型转换为-1的逻辑

This commit is contained in:
smallchill 2025-07-02 19:36:18 +08:00
parent 450cf2e2a2
commit 8139c89007
4 changed files with 125 additions and 4 deletions

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2018-2099, DreamLu 卢春梦 (qq596392912@gmail.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
import java.io.IOException;
/**
* 大数值序列化避免超过js的精度造成精度丢失
*
* @author L.cm
*/
@JacksonStdImpl
public class BigNumberSerializer extends NumberSerializer {
/**
* js 最大值为 Math.pow(2, 53)十进制为9007199254740992
*/
private static final long JS_NUM_MAX = 0x20000000000000L;
/**
* js 最小值为 -Math.pow(2, 53)十进制为-9007199254740992
*/
private static final long JS_NUM_MIN = -0x20000000000000L;
/**
* Static instance that is only to be used for {@link Number}.
*/
public final static BigNumberSerializer instance = new BigNumberSerializer(Number.class);
public BigNumberSerializer(Class<? extends Number> rawType) {
super(rawType);
}
@Override
public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
long longValue = value.longValue();
if (longValue < JS_NUM_MIN || longValue > JS_NUM_MAX) {
gen.writeString(value.toString());
} else {
super.serialize(value, gen, provider);
}
}
}

View File

@ -29,6 +29,15 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("blade.jackson") @ConfigurationProperties("blade.jackson")
public class BladeJacksonProperties { public class BladeJacksonProperties {
/**
* null 转为 字符串转成""数组转为[]对象转为{}数字转为-1
*/
private Boolean nullToEmpty = Boolean.TRUE;
/**
* 响应到前端大数值自动写出为 String避免精度丢失
*/
private Boolean bigNumToString = Boolean.TRUE;
/** /**
* 支持 MediaType text/plain用于和 blade-api-crypto 一起使用 * 支持 MediaType text/plain用于和 blade-api-crypto 一起使用
*/ */

View File

@ -0,0 +1,47 @@
/**
* Copyright (c) 2018-2099, DreamLu 卢春梦 (qq596392912@gmail.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 大整数序列化为 String 字符串避免浏览器丢失精度
*
* <p>
* 前端建议采用
* bignumber https://github.com/MikeMcl/bignumber.js
* decimal.js https://github.com/MikeMcl/decimal.js
* </p>
*
* @author L.cm
*/
public class BladeNumberModule extends SimpleModule {
public static final BladeNumberModule INSTANCE = new BladeNumberModule();
public BladeNumberModule() {
super(BladeNumberModule.class.getName());
// Long BigInteger 采用定制的逻辑序列化避免超过js的精度
this.addSerializer(Long.class, BigNumberSerializer.instance);
this.addSerializer(Long.TYPE, BigNumberSerializer.instance);
this.addSerializer(BigInteger.class, BigNumberSerializer.instance);
// BigDecimal 采用 toString 避免精度丢失前端采用 decimal.js 来计算
this.addSerializer(BigDecimal.class, ToStringSerializer.instance);
}
}

View File

@ -49,7 +49,7 @@ public class MappingApiJackson2HttpMessageConverter extends AbstractReadWriteJac
* @see Jackson2ObjectMapperBuilder#json() * @see Jackson2ObjectMapperBuilder#json()
*/ */
public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper, BladeJacksonProperties properties) { public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper, BladeJacksonProperties properties) {
super(objectMapper, initWriteObjectMapper(objectMapper), initMediaType(properties)); super(objectMapper, initWriteObjectMapper(objectMapper, properties), initMediaType(properties));
} }
private static List<MediaType> initMediaType(BladeJacksonProperties properties) { private static List<MediaType> initMediaType(BladeJacksonProperties properties) {
@ -63,12 +63,18 @@ public class MappingApiJackson2HttpMessageConverter extends AbstractReadWriteJac
return supportedMediaTypes; return supportedMediaTypes;
} }
private static ObjectMapper initWriteObjectMapper(ObjectMapper readObjectMapper) { private static ObjectMapper initWriteObjectMapper(ObjectMapper readObjectMapper, BladeJacksonProperties properties) {
// 拷贝 readObjectMapper // 拷贝 readObjectMapper
ObjectMapper writeObjectMapper = readObjectMapper.copy(); ObjectMapper writeObjectMapper = readObjectMapper.copy();
// 大数字 字符串
if (Boolean.TRUE.equals(properties.getBigNumToString())) {
writeObjectMapper.registerModules(BladeNumberModule.INSTANCE);
}
// null 处理 // null 处理
writeObjectMapper.setSerializerFactory(writeObjectMapper.getSerializerFactory().withSerializerModifier(new BladeBeanSerializerModifier())); if (Boolean.TRUE.equals(properties.getNullToEmpty())) {
writeObjectMapper.getSerializerProvider().setNullValueSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER); writeObjectMapper.setSerializerFactory(writeObjectMapper.getSerializerFactory().withSerializerModifier(new BladeBeanSerializerModifier()));
writeObjectMapper.getSerializerProvider().setNullValueSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
}
return writeObjectMapper; return writeObjectMapper;
} }