原创

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

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

因为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)

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
@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.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一般会有两种方式:
第一种:

@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);
        serializeConfig.put(Long.class, ToStringSerializer.instance);
        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
        fastJsonConfig.setSerializeConfig(serializeConfig);
        fastConverter.setFastJsonConfig(fastJsonConfig);
        converters.add(fastConverter);
    }
}

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

e.g.

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
 */
@Slf4j
public 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"
  ]
}
正文到此结束
本文目录