JSON stringify 和 PostgreSQL bigint 合规性
我正在尝试在我的图书馆
中添加
BigInt
支持,并遇到了JSON.stringify
的问题。
库的性质允许不必担心类型歧义和反序列化,因为序列化的所有内容都会进入服务器,并且永远不需要任何反序列化。
我最初想出了以下简化方法,只是为了抵消 Node.js 向我投掷
TypeError: Do not know how to serialize a BigInt
:
// Does JSON.stringify, with support for BigInt:
function toJson(data) {
return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? v.toString() : v);
}
但是因为它将每个
BigInt
转换成一个字符串,所以每个值最终都用双引号引起来。
是否有任何解决方法,也许是 Node.js 格式化实用程序中的一些技巧,以从
JSON.stringify
生成结果,其中每个 BigInt
将被格式化为开放值?这是 PostgreSQL 理解和支持的,所以我正在寻找一种方法来生成符合 PostgreSQL 的 BigInt
JSON。
例子
const obj = {
value: 123n
};
console.log(toJson(obj));
// This is what I'm getting: {"value":"123"}
// This is what I want: {"value":123}
显然,我不能只将
BigInt
转换为 number
,因为那样我会丢失信息。为此重写整个JSON.stringify
可能太复杂了。
更新
在这一点上,我已经回顾并使用了几个 polyfill,比如这些:
- polyfill-1
- polyfill-2
但它们似乎都是一个尴尬的解决方案,引入这么多代码,然后进行修改以获得
BigInt
支持。我希望找到更优雅的东西。
回答如下:
我最终得到的解决方案...
注入完整的
123n
数字,然后在正则表达式的帮助下取消引号:
function toJson(data) {
return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? `${v}n` : v)
.replace(/"(-?\d+)n"/g, (_, a) => a);
}
它完全满足需要,而且速度很快。唯一的缺点是,如果您在
data
中将一个值设置为类似 123n
的字符串,它将变成一个开放数字,但您可以在上面轻松地将其混淆,变成类似 ${^123^}
或 123-bigint
的东西
,算法很容易做到。
根据问题,该操作并不意味着是可逆的,因此如果您在结果上使用
JSON.parse
,那将是 number
-s,如预期的那样丢失 2^53
和 2^64 - 1
之间的任何内容。
谁说这是不可能的-嗯? :)
更新-1
为了与
JSON.stringify
兼容,undefined
必须导致undefined
。在实际的 pg-promise 实现中,我现在使用 "123#bigint"
模式,以减少意外匹配的可能性。
所以这是最终代码:
function toJson(data) {
if (data !== undefined) {
return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? `${v}#bigint` : v)
.replace(/"(-?\d+)#bigint"/g, (_, a) => a);
}
}
更新-2
通过下面的评论,您可以通过计算与
BigInt
注入匹配的替换次数来确保安全,并在不匹配时抛出错误:
function toJson(data) {
if (data !== undefined) {
let intCount = 0, repCount = 0;
const json = JSON.stringify(data, (_, v) => {
if (typeof v === 'bigint') {
intCount++;
return `${v}#bigint`;
}
return v;
});
const res = json.replace(/"(-?\d+)#bigint"/g, (_, a) => {
repCount++;
return a;
});
if (repCount > intCount) {
// You have a string somewhere that looks like "123#bigint";
throw new Error(`BigInt serialization conflict with a string value.`);
}
return res;
}
}
虽然我个人觉得有点大材小用,但
UPDATE-1
里面的做法已经很不错了