Соответствие JSON stringify и PostgreSQL bigint - PullRequest
1 голос
/ 05 октября 2019

Я пытаюсь добавить поддержку 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 понимает и поддерживает, и поэтому я ищу способ создания 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 для этого, вероятно, было бы слишком сложно.

ОБНОВЛЕНИЕ

На данный момент я рассмотрел и поиграл с несколькими полифиллами, такими как эти:

Но все они кажутся неловким решением, чтобы ввести так много кода, а затем изменить для поддержки BigInt. Я надеюсь найти что-то более элегантное.

1 Ответ

0 голосов
/ 06 октября 2019

Просто пришел ко мне во сне, как это происходит, когда вы слишком много думаете о чем-то, поэтому мне пришлось встать и записать это здесь, прежде чем я забуду ... :)

Мыможет ввести полные 123n числа, а затем снять их с кавычек с помощью RegEx:

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, как и ожидалось.

Кто бы сказал, что это невозможно, а? :)

UPDATE-1

Для совместимости с JSON.stringify, undefined должно привести к undefined. И в рамках фактической реализации pg-обещание я сейчас использую шаблон "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 pattern conflict with a string value.`);
        }
        return res;
    }
}
...