关于前端:为什么JSONparse会损坏大数字如何解决这个问题

39次阅读

共计 4388 个字符,预计需要花费 11 分钟才能阅读完成。

微信搜寻【大迁世界】, 我会第一工夫和你分享前端行业趋势,学习路径等等。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

从 10 多年前 JSON 在线编辑器的晚期开始,用户常常反映编辑器有时会毁坏他们 JSON 文档中的大数字的问题。直到现在,咱们也没能解决这个问题。在这篇文章中,咱们深刻解释了这个问题,并展现如何在 JSON Editor Online 中解决这个问题。

大数字的问题

大多数 Web 利用程序处理来自服务器的数据。这些数据以纯文本的 JSON 文档模式被接管,并被解析成一个 JavaScript 对象或数组,这样咱们就能够读取属性并做一些事件。通常状况下,数据的解析是应用 JSON.parse 函数进行的,该函数内置于 JavaScript 中,十分疾速和不便。

JSON 数据格式极其简略,而且它是 JavaScript 的一个子集。所以它与 JavaScript 齐全能够调换。你能够将一个 JSON 文档粘贴到一个 JavaScript 文件中,这就是无效的 JavaScript。

在 JavaScript 中应用 JSON 应该不会呈现任何问题,但有一种辣手的状况可能会毁坏数据:大数字。这是一个无效的 JSON 字符串:

{"count": 9123372036854000123}

当咱们将其解析为 JavaScript 并读取 "count" 键时,咱们会失去:

9123372036854000000

解析后的数值被毁坏了:最初三位数字被重置为零。这是否是一个问题,取决于这些最初的数字是否的确有意义,但一般来说,晓得这种状况可能会产生,可能会给你一种不难受的感觉。

为什么大数字会被 JSON.parse 毁坏?

9123372036854000123 这样的长数字既是无效的 JSON 也是无效的 JavaScript。当 JavaScript 将数值解析为数字时,事件就出错了。最后,JavaScript 只有一种数字类型。Number。这是一个 64 位的浮点值,相似于 C ++、Java 或 C# 中的 Double 值。这种浮点值能够存储大概 16 位数字。因而,它不能齐全代表像 9123372036854000123 这样的数字,它有 19 位数字。在这种状况下,最初三位数字会失落,毁坏了该值。

在用浮点数存储分数时也会产生同样的状况:当你在 JavaScript 中计算 1/3时,后果是:

0.3333333333333333

在事实中,该值应该有有限的小数,但 JavaScript 的数字在大概 16 位 之后就进行了。

那么,JSON 文档中像 9123372036854000123 这样的大数字是怎么来的呢?嗯,其余语言如 Java 或 C# 的确有其余数字数据类型,如 LongLong 是一个 64 位的值,能够包容最多 20 位的整数。它能包容更多数字的起因是,它不须要像浮点值那样存储指数值。因而,在像 Java 这样的语言中,你能够有一个 Long 值,它不能在 JavaScript 的 Number 类型中正确示意,或者在其余语言中的 Double 类型中正确示意。

JavaScript 的 Number(或者更好:任何浮点数值)还有一些限度:数值能够溢出或下溢。例如,1e+500会变成 Infinity,而1e-500 会变成0。不过,这些限度在理论应用程序中很少成为问题。

如何避免数字被 JSON.parse 毁坏?

多年来,这个用 JavaScript 解析大数字的问题始终是https://jsoneditoronline.org/ 的用户重复要求的。像大多数基于网络的 JSON 编辑器一样,它也应用了本地的 JSON.parse 函数和惯例的 JavaScript 数字,所以它受到了上述的限度。

第一个想法可能是:等等,然而 JSON.parse 有一个可选的 reviver 参数,容许你用不同的形式来解析内容。但问题是,首先文本被解析成一个数字,接下来,它被传递给reviver。所以到那时,曾经太晚了,值曾经被毁坏了。

为了解决这个问题,基本不能应用内置的JSON.parse,必须应用一个不同的 JSON 解析器。对此有各种优良的解决方案:lossless-json、json-bigint、js-jon-bigint 或 json-source-map。

这些库中的大多数都采取了求实的办法,将长数字间接解析为 JavaScript 绝对较新的 BigInt 数据类型。lossless-json库是专门为 JSON Editor Online 开发的。它采取了比 JSON BigInt 解决方案更加灵便和弱小的办法。

默认状况下,lossless-json 将数字解析成一个轻量级的 LosslessNumber 类,该类将数字值作为一个字符串持有。这保留了任何数值,甚至还保留了格式化,比方数值 4.0 中的尾部零。当对其进行操作时,LosslessNumber将被转换为 NumberBigInt,或者在不平安时抛出一个谬误。

该库容许你传递你本人的数字解析器,所以你能够利用你本人的策略来解决数字值。兴许你想把长的数字值转换成 BigInt,或者把数值传给某个BigNumber 库。你能够抉择是否要在数字信息失落时抛出一个异样,或者默默地疏忽某些类别的信息失落。

因而,比拟本地 JSON.parse 函数和lossless-json,会失去以下后果:

import {parse, stringify} from 'lossless-json'
const text = '{"decimal":2.370,"long":9123372036854000123,"big":2.3e+500}'
// JSON.parse will lose some digits and a whole number:
console.log(JSON.stringify(JSON.parse(text)))
// '{"decimal":2.37,"long":9123372036854000000,"big":null}'
// WHOOPS!!!
// LosslessJSON.parse will preserve all numbers and even the formatting:
console.log(stringify(parse(text)))
// '{"decimal":2.370,"long":9123372036854000123,"big":2.3e+500}'

应用 LosslessJSON 解析器是否能解决所有问题?

答案是并不能。这取决于你在解析数据后想做什么,但通常状况下,你想用它做一些事件。在屏幕上显示数据,验证它,比拟它,排序它,等等。例如,在 JSON Editor Online 中,你能够编辑数值,转换文档(查问、过滤、排序等),比拟两个文档,或者依据 JSON 模式验证一个文档。一旦你引入 BigInt 值或LosslessNumbers,你想执行的所有操作都须要反对这些类型的值。

领有 BigInt 值或 LosslessNumbers 的数据很可能给不理解这些数据类型的第三方库带来问题。例如,JSON Editor Online 反对将你的 JSON 数据导出到 CSV,并应用优良的 json2csv 库来实现。

这个库不晓得 BigIntLosslessNumber类型,不会正确串联这些数据类型。为了使其失常工作,蕴含 LosslessNumbersBigInt值的 JSON 数据必须首先被转换为该库所能了解的数据。

即便没有第三方库的参加,与 BigInt 值一起工作也会导致辣手的问题。当对大整数和一般数字的混合操作时,JavaScript 能够默默地将一种数字类型强制转化为另一种,这可能会导致谬误。上面的代码例子显示了这是如何出错的。

const a = 91111111111111e3 // a regular number
const b = 91111111111111000n // a bigint
console.log(a == b) // returns false (should be true)
console.log(a > b) // returns true (should be false)

在这个例子中,你看到两个常数 ab持有雷同的数字值。然而一个是数字,另一个是 BigInt,用这些货色和一般的操作符(如==>)一起应用会导致谬误的后果。

论断:要让大数字在一个应用程序中工作,可能须要大量的致力。因而,最好的方法是尽量避免在一开始就解决这些问题。

如果你真的要解决大数值,你必须应用一个代替的 JSON 分析器,如 lossless-json。为了避免陷入与领有BigIntLosslessNumber数据类型无关的难以调试的问题,应用 TypeScript 明确定义你的数据模型是很有帮忙的。这样,你就能够当时晓得哪些地方须要可能解决这些非凡的数据类型,你就能够采取行动,而不是让你的应用程序默默地失败。

在线 JSON 编辑器当初能够平安地解决大数字了

从今天起,JSON Editor Online 曾经齐全反对大数字,所以你不用再放心损坏的数值。它曾经集成了 lossless-json 库,并确保编辑器的所有性能都能解决大数字:从格式化、排序和查问到导出到 CSV。作为一个副作用,它当初甚至放弃了数字的格式化,而且因为新的 LosslessJSON 解析器,当初能够检测到反复的键。

试一试:https://jsoneditoronline.org/#left=json.%7B%20%22using%22:%20…,%20%22formatted%20number%22:%204.0,%20%22long%22:%209123372036854000123,%20%22large%22:%201e500,%20%22small%22:1e-500%20%7D

当初,应用 lossless-json 有一个毛病:它比原生内置的 JSON.parse 慢得多。这只是大的 JSON 对象或数组的问题,对于大于 10MB 的文件,它可能会很显著。为了仍能顺利地解决大文件,JSON Editor Online 容许你抉择你想应用的解析器,默认状况下,它会主动为你抉择最合适的解析器。

代码部署后可能存在的 BUG 没法实时晓得,预先为了解决这些 BUG,花了大量的工夫进行 log 调试,这边顺便给大家举荐一个好用的 BUG 监控工具 Fundebug。

起源:https://jsoneditoronline.org/indepth/parse/why-does-json-pars…

交换

有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

正文完
 0