问题描述
我一直在使用字符串来表示大于 32 位的解码 JSON 整数。似乎 string_of_int
能够处理大整数输入。因此编写了一个解码器(在 Json.Decode 命名空间中):
id: json |> field("id",int) |> string_of_int,/* 'id' is string */
正在成功处理至少 37 位的整数。
另一方面,编码对我来说很麻烦。远程服务器不接受字符串表示,并且需要一个 int64。是否可以让 bs-json
支持 int64
类型?我希望这样的事情可以发挥作用:
type myData = { id: int64 };
let encodeMyData = (data:myData) => Json.Encode.(object_([("id",int64(myData.id)]))
必须使用自己的编码器不像解码器那么强大,但是......我宁愿不要。
解决方法
您没有确切说明编码有什么问题。 int
编码器除了更改类型外实际上什么都不做,相信 int
值实际上是有效的。所以我认为是 int_of_string
操作导致了问题。但这引出了一个问题,如果您可以成功地将其解码为 int
,为什么还要将其转换为 string
?
这里的根本问题是 JavaScript 没有 64 位整数。 max safe integer 是 253 - 1. JavaScript 实际上根本没有整数,只有 float
s,它可以表示一定范围的整数,但不能有效地进行整数运算,除非它们被转换为 32 位或 64 位 int
。因此无论出于何种原因,可能是一致的溢出处理,在 EcmaScript specification 中决定二进制位运算应该对 32 位整数进行操作。因此,这开启了内部 32 位表示、创建 32 位整数的表示法以及优化整数算法的可能性。
所以你的问题:
将外部 int64 : int64 -> Js.Json.t = "%identity" 添加到编码器文件是否“安全”?
不,因为 JavaScript 中没有 64 位整数表示,int64
值表示为两个 Number
的数组,我相信,但也是一个可能会更改的内部实现细节。仅将其强制转换为 Js.Json.t
不会产生您期望的结果。
那你能做什么?
我建议使用 float
。在大多数方面,这将与 JavaScript 数字完全一样,让您可以访问其全部范围。
或者,您可以使用 nativeint
,它的行为应该类似于 float
,除了除法之外,结果被截断为 32 位整数。
最后,您还可以通过直接使用几个轻量级 JavaScript 函数来实现自己的 int_of_string
以创建技术上超出范围的 int
,但我真的不建议这样做:
let bad_int_of_string = str =>
str |> Js.Float.fromString |> Js.Math.floor_int;