最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

JSON stringify 和 PostgreSQL bigint 合规性

运维笔记admin8浏览0评论

JSON stringify 和 PostgreSQL bigint 合规性

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
里面的做法已经很不错了

发布评论

评论列表(0)

  1. 暂无评论