在 JSON 之间序列化 BigDecimal、BigNumber、BigInt 等
Serialise BigDecimal, BigNumber, BigInt etc. to and from JSON
我可以使用什么类型和什么序列化模式来确保我的 JavaScript 程序在 运行 时具有 任意精度数字 ,但是 状态被序列化到 JSON 或从 JSON 反序列化,痛苦最小?
我正在编写一个应用程序,它大量使用精度高于 ECMAScript 本机 Number
类型所允许的精度的数字。所以我需要使用自定义类型(目前 BigNumber
)来表示这些值。
我非常期待提议的 BigInt
类型,当它作为标准实现时,将对许多应用程序(包括我正在编写的应用程序)有所改进。然而,这对 JSON 一点帮助都没有,它对 BigInt
的了解比对 BigNumber
.
的了解更多
无论我选择哪种自定义类型,在整个应用程序状态中都有很多这种类型的值。如此之多以至于值得考虑在序列化/反序列化层中使用自定义挂钩来处理将其转换为 JSON.
大概 JSON 文档必须将值表示为 JSON 本机类型(例如 Object
或 String
实例)。那么,在反序列化整个复杂应用程序状态时,如何可靠地识别所有其他实例并将这些实例反序列化为正确的 BigInt
或 BigNumber
类型的正确值?
我如何序列化该状态,以便任何 BigNumber
(或插入一些其他任意精度数字类型)值在序列化/反序列化过程中可靠地存活下来,以更正正确类型的值?
一个可能的解决方案是 the Granola library for Node.js。
granola provides a JSON compatible stringifier and parser with support for modern language and object primitives.
这需要:
- 将应用程序从使用
BigNumber
值转换为 BigInt
值。
- 等待工具在整个构建工具链中支持
BigInt
。
如果修改类型(通过向原型添加 属性)是可行的,则有一个 toJSON
hook from JSON.stringify
,专门用于帮助自定义类型与 JSON 序列化协作。
If an object being stringified has a property named toJSON
whose value is a function, then the toJSON()
method customizes JSON stringification behavior: instead of the object being serialized, the value returned by the toJSON()
method when called will be serialized.
因此您可以向 BigNumber
添加一个新方法 class:
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}'
然后您可以在反序列化时识别这些特定对象,使用 the reviver
parameter of JSON.parse
:
If a reviver
is specified, the value computed by parsing is transformed before being returned. Specifically, the computed value and all its properties (beginning with the most nested properties and proceeding to the original value itself) are individually run through the reviver
. Then it is called, with the object containing the property being processed as this
, and with the property name as a string, and the property value as arguments. [If the return value is not undefined
], the property is redefined to be the return value.
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 ] }, … }
我可以使用什么类型和什么序列化模式来确保我的 JavaScript 程序在 运行 时具有 任意精度数字 ,但是 状态被序列化到 JSON 或从 JSON 反序列化,痛苦最小?
我正在编写一个应用程序,它大量使用精度高于 ECMAScript 本机 Number
类型所允许的精度的数字。所以我需要使用自定义类型(目前 BigNumber
)来表示这些值。
我非常期待提议的 BigInt
类型,当它作为标准实现时,将对许多应用程序(包括我正在编写的应用程序)有所改进。然而,这对 JSON 一点帮助都没有,它对 BigInt
的了解比对 BigNumber
.
无论我选择哪种自定义类型,在整个应用程序状态中都有很多这种类型的值。如此之多以至于值得考虑在序列化/反序列化层中使用自定义挂钩来处理将其转换为 JSON.
大概 JSON 文档必须将值表示为 JSON 本机类型(例如 Object
或 String
实例)。那么,在反序列化整个复杂应用程序状态时,如何可靠地识别所有其他实例并将这些实例反序列化为正确的 BigInt
或 BigNumber
类型的正确值?
我如何序列化该状态,以便任何 BigNumber
(或插入一些其他任意精度数字类型)值在序列化/反序列化过程中可靠地存活下来,以更正正确类型的值?
一个可能的解决方案是 the Granola library for Node.js。
granola provides a JSON compatible stringifier and parser with support for modern language and object primitives.
这需要:
- 将应用程序从使用
BigNumber
值转换为BigInt
值。 - 等待工具在整个构建工具链中支持
BigInt
。
如果修改类型(通过向原型添加 属性)是可行的,则有一个 toJSON
hook from JSON.stringify
,专门用于帮助自定义类型与 JSON 序列化协作。
If an object being stringified has a property named
toJSON
whose value is a function, then thetoJSON()
method customizes JSON stringification behavior: instead of the object being serialized, the value returned by thetoJSON()
method when called will be serialized.
因此您可以向 BigNumber
添加一个新方法 class:
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}'
然后您可以在反序列化时识别这些特定对象,使用 the reviver
parameter of JSON.parse
:
If a
reviver
is specified, the value computed by parsing is transformed before being returned. Specifically, the computed value and all its properties (beginning with the most nested properties and proceeding to the original value itself) are individually run through thereviver
. Then it is called, with the object containing the property being processed asthis
, and with the property name as a string, and the property value as arguments. [If the return value is notundefined
], the property is redefined to be the return value.
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 ] }, … }