Сериализация BigDecimal, BigNumber, BigInt и т. Д. В и из JSON - PullRequest
0 голосов
/ 12 ноября 2018

Какие типы и какой шаблон сериализации я могу использовать, чтобы убедиться, что у меня есть числа произвольной точности в моей программе JavaScript во время работы, но состояние сериализуется в / десериализовано из JSON с минимумом боли?

Я пишу приложение, которое интенсивно использует числа с большей точностью, чем разрешено родным типом Number ECMAScript. Поэтому мне нужно использовать пользовательский тип (в настоящее время BigNumber) для представления этих значений.

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

Какой бы пользовательский тип я ни выбрал, существует множество значений этого типа во всем состоянии приложения. Настолько, что стоит рассмотреть пользовательский хук на уровне сериализации / десериализации, который будет обрабатывать его преобразование в / из JSON.

Предположительно, документ JSON должен будет представлять значения в виде собственных типов JSON (например, Object или String экземпляров). Как тогда при десериализации всего сложного состояния приложения надежно распознать и десериализовать эти экземпляры среди всех остальных, чтобы получить правильное значение правильного типа BigInt или BigNumber?

Как я могу сериализовать это состояние, чтобы любое значение BigNumber (или вставить какой-либо другой тип числа произвольной точности) надежно переживало процесс сериализации / десериализации, исправляя значения правильного типа?

Ответы [ 2 ]

0 голосов
/ 12 ноября 2018

Если возможно изменение типа (путем добавления свойства к прототипу), существует toJSON ловушка из JSON.stringify, специально разработанная, чтобы помочь пользовательским типам взаимодействовать с сериализацией JSON.

Если объект, подлежащий строковой обработке, имеет свойство с именем toJSON, значением которого является функция, то метод toJSON() настраивает поведение строковой классификации JSON: вместо сериализуемого объекта значение, возвращаемое методом toJSON() при вызове будет сериализовано.

Таким образом, вы можете добавить новый метод в класс BigNumber:

BigNumber.prototype.toJSON = function toJSON(key) {
    return {
        _type: 'BigNumber',
        _data: Object.assign({}, this),
    };
};

state = {
    lorem: true,
    ipsum: "Consecteur non dibale",
    dolor: new BigNumber(107.58),
    sit: { spam: 5, eggs: 6, beans: 7 },
    amet: false,
};
serialisedState = JSON.stringify(state);
console.debug("serialisedState:", serialisedState);
//  → '{"lorem":true,"ipsum":"Consecteur non dibale","dolor":{"_type":"BigNumber","_data":{"s":1,"e":2,"c":[1,0,7,5,8]}},"sit":{"spam":5,"eggs":6,"beans":7},"amet":false}'

Затем вы можете распознать эти конкретные объекты при десериализации, используя параметр reviver JSON.parse:

Если указано reviver, значение, вычисленное при разборе, будет преобразовано перед возвратом. В частности, вычисленное значение и все его свойства (начиная с самых вложенных свойств и заканчивая самим исходным значением) по отдельности проходят через reviver. Затем вызывается объект, содержащий свойство, обрабатываемое как this, с именем свойства в виде строки и значением свойства в качестве аргументов. [Если возвращаемое значение не undefined], свойство переопределяется как возвращаемое значение.

function reviveFromJSON(key, value) {
    let result = value;
    if (
        (typeof value === 'object' && value !== null)
            && (value.hasOwnProperty('_type'))) {
        switch (value._type) {
        case 'BigNumber':
            result = Object.assign(new BigNumber(0), value._data);
        }
    }
    return result;
}

state = JSON.parse(serialisedState, reviveFromJSON);
console.debug("state:", state);
// → { … dolor: BigNumber { s: 1, e: 2, c: [ 1, 0, 7, 5, 8 ] }, … }
0 голосов
/ 12 ноября 2018

Одним из возможных решений является библиотека Granola для Node.js .

granola предоставляет JSON-совместимый строковый преобразователь и анализатор с поддержкой современного языка и объектных примитивов.

Для этого потребуется:

  • Преобразование приложения из значений BigNumber в значения BigInt.
  • Подождите, пока инструменты поддержат BigInt во всей цепочке инструментов сборки.
...