原创
Long和Long类型集合前端精度丢失解决办法锦集以及自定义JSON序列化方法
温馨提示:
本文最后更新于 2020年02月24日,已超过 1,733 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我。
因为JS解析整型的时候是有最大值的,Number.MAX_SAFE_INTEGER
常量表示在 JavaScript 中最大的安全整数(maxinum safe integer)(253 - 1)
Number.MAX_SAFE_INTEGER // 9007199254740991
Math.pow(2, 53) - 1 // 9007199254740991
最大长度是16位数,超过了就解析不正常了,会丢失精度。
我后端是用的雪花算法生成的20位的唯一ID,我返回给前端的时候,例如:
我返回的是Long类型的,但是前端接收之后精度丢失,导致和我后端给的不一致,解决办法就是使用String类型的。
方法1-后端传输JSON格式化为String类型的
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long aliyunOssFileId;
@JsonFormat(shape = JsonFormat.Shape.STRING)
作用就是将JSON数据的此字段格式化为字符串类型,保证前端超过16位不会出现精度丢失问题!
但是,如果有很多Long类型的话,要一个一个去改,也太累了,Spring MVC中默认是使用了Jackson的,可以通过重写转换器解决。
方法2-重写Jackson转换器(配置全局生效)
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
/**
* Long类型转String类型
*
* 解决前端Long类型精度丢失问题(js解析只能解析到16位)
*
* @param converters
* @author Zhaopo Liu
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter =
new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
// simpleModule.addSerializer(long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(jackson2HttpMessageConverter);
converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
}
}
方法3-实现Jackson2ObjectMapperBuilderCustomizer
作为Bean(配置全局生效)
和前面说的方法重写Jackson转换器也是一个道理的。
参考:
@Configuration
public class JacksonConfig {
/**
* Callback interface that can be implemented by beans wishing to further customize the {@link
* ObjectMapper} via {@link Jackson2ObjectMapperBuilder} retaining its default auto-configuration.
*
* <pre>
* 在此处{@link Jackson2ObjectMapperBuilderCustomizer}有两种方法,
* 设置Long以及BigInteger类型序列化为String类型,避免雪花算法Long类型返回前端可能导致精度丢失问题。
* - 方式1:
* {@code
* SimpleModule simpleModule = new SimpleModule();
* // simpleModule.addSerializer(long.class, ToStringSerializer.instance);
* simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
* simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
* simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
* builder.modules(simpleModule);
* }
* - 方式2:
* {@code
* // builder.serializerByType(long.class, ToStringSerializer.instance);
* builder.serializerByType(Long.class, ToStringSerializer.instance);
* builder.serializerByType(Long.TYPE, ToStringSerializer.instance);
* builder.serializerByType(BigInteger.class, ToStringSerializer.instance)
* }
* </pre>
*
* @return {@link Jackson2ObjectMapperBuilderCustomizer}
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
SimpleModule simpleModule = new SimpleModule();
// simpleModule.addSerializer(long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
builder.modules(new Jdk8Module(), new JavaTimeModule(), simpleModule);
};
}
}
方法4-如果是FastJson的话(配置全局生效)
在Spring Boot中将Jackson替换为fastjson一般会有两种方式:
第一种:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public HttpMessageConverters fastJsonHttpMessageConverter() {
return new HttpMessageConverters(new FastJsonHttpMessageConverter());
}
}
第二种:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastConverter =
new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
fastConverter.setFastJsonConfig(fastJsonConfig);
converters.add(fastConverter);
}
}
替换成fastjson之后,对于精度丢失问题,解决方法如下:
@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastConverter =
new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
SerializeConfig serializeConfig = SerializeConfig.globalInstance;
serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
// simpleModule.addSerializer(long.class, ToStringSerializer.instance);
serializeConfig.put(Long.class, ToStringSerializer.instance);
serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
fastJsonConfig.setSerializeConfig(serializeConfig);
fastConverter.setFastJsonConfig(fastJsonConfig);
converters.add(fastConverter);
}
}
方法5-List<Long>
类型精度丢失问题
最好的方式就是,将List<Long>
改为List<String>
方式,这样子啥事没有,性能也高,但是我就是想多折腾:
1.数组转换为String显示(不推荐)
直接使用官方的即可:
@JsonSerialize(using = ToStringSerializer.class)
private List<Long> roleIds;
前端显示的,这明显需要前端特殊处理,个人不太喜欢这样子:
{
"roleIds": "[1333010224414613506, 1333010224481722369]"
}
2.自定义序列化方式转换为String数组(推荐)
自定义一个JSON序列化方式:
public class ListLongToStringArrayJsonSerializer extends JsonSerializer<List<Long>> {
@Override
public void serialize(List<Long> values, JsonGenerator gen, SerializerProvider serializers) throws IOException {
String[] newValues =
ObjectUtil.defaultIfNull(values, Collections.emptyList()).stream()
.map(String::valueOf)
.toArray(String[]::new);
gen.writeArray(newValues, 0, newValues.length);
}
}
特别注意:此处的gen.writeArray(newValues, 0, newValues.length);
,类型为String的,Jackson在2.11版本之后才支持此String类型的。
使用方式:
@JsonSerialize(using = LongToJsonSerializer.class)
private List<Long> roleIds;
结果:
{
"roleIds": [
"1333010224414613506",
"1333010224481722369"
]
}
方法6-前端使用String类型来接收
e.g.
aliyunOssFileId: ''
- 本文标签: Vue Java SpringBoot
- 本文链接: http://www.lzhpo.com/article/106
- 版权声明: 本文由lzhpo原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权