当前位置: 首页 > news >正文

处理Long类型长度超长导致前端精度丢失问题

1,问题场景

         后端返回的Long类型的数据,超10000000000000000,前端处理的时候,数据被截断了。比如tchId: 11073477511443988481, 前端根据tchId获取下一环节信息的时候,传的tchId变成了11073477511443988400,导致报错。

    主要是因为JavaScript中的Number类型无法精确表示超过其安全整数范围(即-9007199254740991(-2^53 + 1)到9007199254740991(2^53 - 1))的整数值。因此,当后端Java的Long类型数据超过这个范围时,前端JavaScript在解析这些数据时就会丢失精度。

        因此当Long类型超过的时候,需要把Long类型,转成字符串返回给前端。

2,处理:

1,处理方法1:字段加注解

        可以使用Jackson库的@JsonFormat注解来将Long类型字段序列化为字符串类型。这样,在数据传输到前端时,就会以字符串的形式进行传输,从而避免了精度丢失的问题,如代码字段返回转换成字符串。

import com.fasterxml.jackson.annotation.JsonFormat;

public class Tache {
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    private Long tchld;
}

这种就只适合指定的字段,不通用。如果要设置未全局要怎么做?

2,处理方法2:全局处理

        为了更加便捷地解决这个问题,使用全局配置,将所有Long类型的数据都转换为字符串类型。通过配置Jackson的ObjectMapper来实现

引用:

implementation 'com.fasterxml.jackson.core:jackson-core:2.13.5'

判断处理:
public class StringSerializer extends StdSerializer<Object> {

    private static Logger logger = LoggerFactory.getLogger(StringSerializer.class);

    public final static StringSerializer instance = new StringSerializer();

    public StringSerializer() {
        super(Object.class);
    }

    public StringSerializer(Class<?> handledType) {
        super(handledType, false);
    }

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (value != null && value instanceof Long) {
            if (((Long) value).longValue() >= 10000000000000000L) {
                gen.writeString(value.toString());
            } else {
                gen.writeNumber((Long) value);
            }
        }
    }
}

配置:
@Configuration
@EnableWebMvc
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }


    @Bean
    public ObjectMapper jacksonObjectMapperCustomization() {
        TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai");
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().timeZone(timeZone);
        return builder.build();
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(c -> c instanceof MappingJackson2HttpMessageConverter);
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(
                jacksonObjectMapperCustomization());
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, StringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, StringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
        converters.add(jackson2HttpMessageConverter);
        converters.add(new ByteArrayHttpMessageConverter());
        converters.add(new FormHttpMessageConverter());
        converters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
    }


    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("LOCALE");
        return localeChangeInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }

    @Bean
    protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
        return new RequestMappingHandlerMapping() {
            @Override
            protected boolean isHandler(Class<?> beanType) {
                return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class)
                        || (AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)
                        && !AnnotatedElementUtils.hasAnnotation(beanType, FeignClient.class)));
            }
        };
    }


    @Bean
    public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {
        List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
        Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
        allEndpoints.addAll(webEndpoints);
        allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
        allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
        String basePath = webEndpointProperties.getBasePath();
        EndpointMapping endpointMapping = new EndpointMapping(basePath);
        boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
        return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
    }

    private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
        return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
    }
}

处理的代码:

simpleModule.addSerializer(Long.class, StringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, StringSerializer.instance);

总结:

        如果是只有某些指定数据进行转换,就直接加注解的方式,要全局的话,就用全局的配置去处理。

相关文章:

  • Python用户管理系统深度解析(附源码):从类设计到安全实现的完整指南
  • 「数据可视化 D3系列」入门第一章:Hello D3.js
  • 数据库实战篇,JSON对象在Kooboo中的实际应用(二)
  • SQL注入之时间盲注攻击流程详解
  • SLAM文献之DM-VIO: Delayed Marginalization Visual-Inertial Odometry
  • 大模型之Hugging Face
  • 信奥还能考吗?未来三年科技特长生政策变化
  • 【开发教程】学生团队项目开发协调管理文档库构建以及使用指南
  • #4 为什么要物联以及 物联网的整体结构
  • linux tracepoint系列宏定义(TRACE_EVENT,DEFINE_TRACE等)展开过程分析之三 define_trace.h头文件
  • 【blender小技巧】Blender导出带贴图的FBX模型,并在unity中提取材质模型使用
  • telepresence使用指南
  • 【LH-开发记录】
  • Dockerfile 学习指南和简单实战
  • 一文介绍关于多模态的基础知识 !!
  • PHP弱类型hash比较缺陷
  • 高速连接器设计的技术挑战和解决方案
  • Dify智能体平台源码二次开发笔记(5) - 多租户的SAAS版实现(2)
  • 添加登录和注册功能
  • 图像预处理(OpenCV)-part2
  • 上海:全面建设重复使用火箭创新高地、低成本商业卫星规模制造高地
  • 拖车10公里收1900元?货车司机质疑收费过高,潮州饶平县市监局已介入
  • 美媒称特朗普考虑大幅下调对华关税、降幅或超一半,外交部回应
  • 刺激视网膜可让人“看”到全新颜色
  • 兰斯莫斯想在雅典卫城拍《拯救地球》,希腊官方:价值观不符
  • 叶辛秦文君进校园推广阅读