有没有办法使用 bs-json 对大于 32 位的整数值进行编码?

问题描述

我一直在使用字符串来表示大于 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 实际上根本没有整数,只有 floats,它可以表示一定范围的整数,但不能有效地进行整数运算,除非它们被转换为 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;