后端数据返回number类型的id,前端显示精度丢失了,怎么办?

实际问题

# 起因

在工作中遇到这样一个问题,后端传来JSON字符串,前端放到页面的code编辑器中格式化展示。

如果直接赋值展示,code编辑器中不会进行格式化,只会是一长串的字符串缩在一起,需要前端将其解析后让数据格式化展示。

代码方面,前端先将接口返回的JSON字符串用JSON.parse转为对象类型,然后再用JSON.stringify,第三个参数传\t缩进,转为JSON类型,再给code赋值,就能在code编辑器中格式化展现数据,如图(隐私信息已打码)

# 问题

但问题在于,如图中的cardNodocumentId,后端可能传来的是number类型,前端在JSON.parse后,数据精度就丢失了

'{"id":3021241541248712345}',JSON.parse后就会变成{id: 3021241541248712000}

首先想到的解决办法,就是叫后端更改数据类型,id之类的怎么看都应该是定义为string类型啊

然而,后端说,这个接口是第三方接口,他们也改不了,所以只能前端解决。。。

第三方接口返回的数据多种多样,质量很差,主要有这两个问题:

  1. 不一定是JSON类型,导致JSON.parse解析报错;

  2. 传来超过js精度的大数类型,导致JSON.parse后数据精度丢失,后几位为000

# 解决方案

想到的解决方案为以下两个

# 1.通过第三方插件 json-bigint (opens new window)

部分代码如下:

import JSONbig from 'json-bigint'

const parseRes = JSONbig.parse(jsonStr)
const stringifyRes = JSONbig.stringify(parseRes)
this.codeData = stringifyRes

优点:
1.使用起来简单,方便快捷,为较成熟的第三方库,有人进行维护;
2.两次转化后并没有改变数据的数据类型,大数仍然是number类型。

缺点:
1.使用第三方库,需下载引入依赖,需要考虑你的leader是否允许使用这个第三方库;
2.更重要的是,在这个项目中,是为了在code编辑器中反显正确数值,但是这里的处理尽管parseRes里的大数值被转为了BigInt类型,stringifyRes里也是正确的数值,但给编辑器赋值后,大数精度还是丢失了。就像是在浏览器控制台,随便输入一个大数,一回车,返回的就是精度丢失的数值,所以这个方法在该项目不可行

别的场景下这个库能用,可以做首选解决方案,但是不行的话就只能降级为方法2了

# 2.用正则将大数类型转为字符串

JSON.parse前,通过正则,将大数类型转为字符串,但需要考虑很多场景:

  1. js能处理的最大整数为Math.pow(2, 53),十进制即 9007199254740992(16位),所以超过15位的数就要做转化

  2. 后端返回的数据,可能是仅为大数的;也可能是超过15数,但其类型已经是字符串的,即前面就有单双引号;也可能是一串网址字符串,只是中间一串超过15位的数字的。

所以正则就需要考虑这些来写,可能还有很多其他情况,所以就要根据实际业务不断完善正则了

最后处理完整代码如下:

// 若是返回的为仅数字的,就不需要转了
  if (/^\d+$/.test(jsonStr)) {
    this.codeData = jsonStr
  } else {
    // 对json字符串中超过15位的大数进行转为字符串
    const conversionJsonStr = jsonStr.replace(/([^"'\\u\/0-9])([0-9]{15,})/g, '$1"$2"')
    try {
      // 转为js对象
      const jsObj = JSON.parse(conversionJsonStr)
      // 再转为格式化字符串
      this.codeData = JSON.stringify(jsObj, null, '\t')
    } catch (error) {
      // 接口数据非JSON的情况,或是我们转化的字符串,导致JSON.parse报错的情况
      console.log(error)
      // 对于这些JSON.parse无法处理报错的数据,就只能先直接原样输出
      // 数据问题就罢了,转化的问题就只能后续再不断补充完善了
      this.codeData = jsonStr
    }
  }

页面的code编辑器使用的是vue-codemirror这个插件

Wonderwall
Oasis