JSON stringify 和 PostgreSQL bigint 合规性
JSON stringify and PostgreSQL bigint compliance
I am trying to add BigInt
support within my library 和 运行 与 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 理解和支持的,所以我正在寻找一种方法来生成 JSON 和 BigInt
,这与 PostgreSQL 兼容。
例子
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,比如这些:
但它们似乎都是一个笨拙的解决方案,引入这么多代码,然后修改以获得 BigInt
支持。我希望找到更优雅的东西。
我最终得到的解决方案...
注入完整的 123n
数字,然后在 RegEx 的帮助下取消引用:
function toJson(data) {
return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? `${v}n` : v)
.replace(/"(-?\d+)n"/g, (_, a) => a);
}
它完全可以满足需要,而且速度很快。唯一的缺点是,如果您在 data
中将一个值设置为类似 123n
的字符串,它将成为一个开放数字,但您可以轻松地将其混淆为 [=16] =],或者123-bigint
,算法很容易做到。
根据问题,该操作并不意味着是可逆的,因此如果您对结果使用 JSON.parse
,则结果将是 number
-s,丢失 [=20] 之间的任何内容=] 和 2^64 - 1
,正如预期的那样。
谁说这是不可能的-嗯? :)
UPDATE-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);
}
}
UPDATE-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
中的方法已经足够好了。
I am trying to add BigInt
support within my library 和 运行 与 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 理解和支持的,所以我正在寻找一种方法来生成 JSON 和 BigInt
,这与 PostgreSQL 兼容。
例子
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,比如这些:
但它们似乎都是一个笨拙的解决方案,引入这么多代码,然后修改以获得 BigInt
支持。我希望找到更优雅的东西。
我最终得到的解决方案...
注入完整的 123n
数字,然后在 RegEx 的帮助下取消引用:
function toJson(data) {
return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? `${v}n` : v)
.replace(/"(-?\d+)n"/g, (_, a) => a);
}
它完全可以满足需要,而且速度很快。唯一的缺点是,如果您在 data
中将一个值设置为类似 123n
的字符串,它将成为一个开放数字,但您可以轻松地将其混淆为 [=16] =],或者123-bigint
,算法很容易做到。
根据问题,该操作并不意味着是可逆的,因此如果您对结果使用 JSON.parse
,则结果将是 number
-s,丢失 [=20] 之间的任何内容=] 和 2^64 - 1
,正如预期的那样。
谁说这是不可能的-嗯? :)
UPDATE-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);
}
}
UPDATE-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
中的方法已经足够好了。