Node.js JSON字符串化和PostgreSQL bigint遵从性
,并在JSON.stringify中遇到问题 库的性质允许不必担心类型歧义和反序列化,因为所有序列化的内容都会进入服务器,并且不需要任何反序列化 我最初提出了以下简化方法,只是为了抵消Node.js抛出的Node.js JSON字符串化和PostgreSQL bigint遵从性,node.js,postgresql,stringify,bigint,Node.js,Postgresql,Stringify,Bigint,,并在JSON.stringify中遇到问题 库的性质允许不必担心类型歧义和反序列化,因为所有序列化的内容都会进入服务器,并且不需要任何反序列化 我最初提出了以下简化方法,只是为了抵消Node.js抛出的TypeError:不知道如何对我的BigInt进行序列化: // Does JSON.stringify, with support for BigInt: function toJson(data) { return JSON.stringify(data, (_, v) =>
TypeError:不知道如何对我的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理解和支持的,因此我正在寻找一种使用BigInt
生成JSON的方法,该方法与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
可能太复杂了
更新
此时,我已经回顾并使用了几种多边形填充,如以下:
但它们似乎都是一个笨拙的解决方案,引入这么多代码,然后修改以获得BigInt
支持。我希望找到更优雅的解决方案。我最终得到的解决方案
注入完整的123n
数字,然后在RegEx的帮助下取消引用这些数字:
function toJson(data) {
return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? `${v}n` : v)
.replace(/"(-?\d+)n"/g, (_, a) => a);
}
它能做需要的事情,而且速度很快。唯一的缺点是,如果您的数据中有一个值设置为类似123n
的字符串,它将成为一个开放数字,但您可以很容易地将其混淆为类似${^123^}
或123 bigint
的内容,该算法很容易实现
根据这个问题,该操作并不意味着是可逆的,因此如果对结果使用JSON.parse
,这些结果将是number
-s,正如预期的那样,丢失2^53
和2^64-1
之间的任何内容
谁说这是不可能的-嗯?:)
更新-1
为了与JSON.stringify
兼容,未定义
必须导致未定义
。在实际实现中,我现在使用了“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
中的方法已经足够好了。我也不认为用原生JSON.stringify
实现这一点的方法(除此之外)。我很确定,通过剥离不必要的功能(缩进、ES3兼容性),可以大大简化polyfills的代码。@Bergi我一直在得出相同的结论,但仍然希望是错误的,以避免混淆polyfills的修改。由于这个东西非常通用,它会要求自己的包,我想。@Bergi看到了我自己的答案,在我得到了所有“不可能的”反馈之后:))我建议采用@Melchia的方法,让toJson
回调返回一个类似{“$type”:“bigint”,“value:”…
,然后将其与正则表达式匹配。这将使意外匹配几乎不可能。JSON很可能包含用户控制的任意字符串数据,这些数据可以通过手工制作来进行注入,而完整的对象由用户控制则是很不寻常的。@Bergi实际上,他的建议毫无意义,因为它没有解决任何问题。他根本没有建议使用正则表达式。现在我使用的是“123#bigint”
,它足够独特,不会与随机字符串混合。我认为把它转换成一个对象并没有什么好处。它将以相同的字符串结尾,对于ReGEX进行解析是更复杂的。不同之处在于,我认为…也许它不太可能随机出现,但对于控制对象中某些字符串值的攻击者来说,它似乎很容易注入。正则表达式会更长,是的,但不会更复杂。作为一种额外的安全措施,您可以添加一个计数器,用于计算在对象中遇到多少个bigint,以及另一个计数器,用于计算应用replace
回调的频率,如果不匹配,则抛出异常,而不是在数据库中存储无效数据。@Bergi在替换计数不匹配时抛出错误实际上是个好主意。早些时候我自己也在考虑。