10

Long和Long类型集合前端精度丢失解决办法锦集以及自定义JSON序列化方法

 3 years ago
source link: http://www.lzhpo.com/article/106
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

LongList<Long>前端精度丢失解决办法锦集以及自定义JSON序列化方法

因为JS解析整型的时候是有最大值的,Number.MAX_SAFE_INTEGER 常量表示在 JavaScript 中最大的安全整数(maxinum safe integer)(253 - 1)

Number.MAX_SAFE_INTEGER // 9007199254740991Math.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)

import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.module.SimpleModule;import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;import com.leyou.order.interceptor.LoginInterceptor;import com.leyou.order.properties.JwtProperties;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.http.converter.StringHttpMessageConverter;import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import java.math.BigInteger;import java.nio.charset.Charset;import java.util.ArrayList;import java.util.List;@Configuration@EnableWebMvcpublic 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.TYPE, ToStringSerializer.instance);        objectMapper.registerModule(simpleModule);        jackson2HttpMessageConverter.setObjectMapper(objectMapper);        converters.add(jackson2HttpMessageConverter);        converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));    }}

方法3-如果是FastJson的话

在Spring Boot中将Jackson替换为fastjson一般会有两种方式:
第一种:

@Configurationpublic class WebConfig extends WebMvcConfigurerAdapter {    @Bean    public HttpMessageConverters fastJsonHttpMessageConverter() {        return new HttpMessageConverters(new FastJsonHttpMessageConverter());    }}
@Configurationpublic 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@Configurationpublic 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);        serializeConfig.put(Long.class, ToStringSerializer.instance);        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);        fastJsonConfig.setSerializeConfig(serializeConfig);        fastConverter.setFastJsonConfig(fastJsonConfig);        converters.add(fastConverter);    }}

方法4-前端使用String类型来接收

aliyunOssFileId: ''

方法5-List<Long>类型精度丢失问题

最好的方式就是,将List<Long>改为List<String>方式,这样子啥事没有,性能也高,但是我就是想多折腾:

1.数组转换为String显示(不推荐)

直接使用官方的即可:

@JsonSerialize(using = ToStringSerializer.class)private List<Long> roleIds;

前端显示的,这明显需要前端特殊处理,个人不太喜欢这样子:

{  "roleIds": "[1333010224414613506, 1333010224481722369]"}

2.自定义序列化方式转换为String数组(推荐)

自定义一个JSON序列化方式:

package com.lzhpo.common.serializer;import com.fasterxml.jackson.core.JsonGenerator;import com.fasterxml.jackson.databind.JsonSerializer;import com.fasterxml.jackson.databind.SerializerProvider;import com.lzhpo.common.enums.ResultStatus;import com.lzhpo.common.exception.CustomException;import lombok.extern.slf4j.Slf4j;import java.io.IOException;import java.util.List;import java.util.Optional;/** * {@code List<Long>} to {@code List<String>} * * <pre> * e.g: *   -  @JsonSerialize(using = ListLongToStringArrayJsonSerializer.class) *      private List<Long> roleIds; * </pre> * * @author Zhaopo Liu */@Slf4jpublic class ListLongToStringArrayJsonSerializer extends JsonSerializer<List<Long>> {    @Override    public void serialize(List<Long> values, JsonGenerator gen, SerializerProvider serializers) throws IOException {        log.info("List<Long> to String[] created by values:{}", values);        try {            gen.writeArray(                    Optional.ofNullable(values.isEmpty() ? null : values)                            .map(list -> list.stream().map(String::valueOf).toArray(String[]::new))                            .orElseGet(() -> new String[0]), 0, values.size());        } catch (IOException e) {            throw new CustomException(ResultStatus.FAIL, "Convert JSON string array error:" + e.getMessage());        }    }}

使用方式:

@JsonSerialize(using = LongToJsonSerializer.class)private List<Long> roleIds;
{  "roleIds": [    "1333010224414613506",    "1333010224481722369"  ]}

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK