正确的往返序列化
只需复制并使用它。或者使用the npm package。
const serialize = (value) => JSON.stringify(value, stringifyReplacer);
const deserialize = (text) => JSON.parse(text, parseReviver);
// License: CC0
function stringifyReplacer(key, value) {
if (typeof value === "object" && value !== null) {
if (value instanceof Map) {
return {
_meta: { type: "map" },
value: Array.from(value.entries()),
};
} else if (value instanceof Set) { // bonus feature!
return {
_meta: { type: "set" },
value: Array.from(value.values()),
};
} else if ("_meta" in value) {
// Escape "_meta" properties
return {
...value,
_meta: {
type: "escaped-meta",
value: value["_meta"],
},
};
}
}
return value;
}
function parseReviver(key, value) {
if (typeof value === "object" && value !== null) {
if ("_meta" in value) {
if (value._meta.type === "map") {
return new Map(value.value);
} else if (value._meta.type === "set") {
return new Set(value.value);
} else if (value._meta.type === "escaped-meta") {
// Un-escape the "_meta" property
return {
...value,
_meta: value._meta.value,
};
} else {
console.warn("Unexpected meta", value._meta);
}
}
}
return value;
}
为什么这很难?
应该可以输入任何类型的数据,获取有效的 JSON,然后从那里获取正确地重建输入。
这意味着处理
- 以对象作为键的地图
new Map([ [{cat:1}, "value"] ])。这意味着任何使用 Object.fromEntries 的答案都可能是错误的。
- 具有嵌套地图的地图
new Map([ ["key", new Map([ ["nested key", "nested value"] ])] ])。许多答案通过仅回答问题而不处理任何其他问题来回避这一点。
- 混合对象和地图
{"key": new Map([ ["nested key", "nested value"] ]) }。
除了这些困难之外,序列化格式必须是明确的。否则不能总是重建输入。最佳答案有一个失败的测试用例,见下文。
因此,我写了这个改进版本。它使用 _meta 而不是 dataType 来减少冲突,如果确实发生冲突,它实际上会明确处理。希望代码也足够简单,可以轻松扩展以处理其他容器。
然而,我的回答并没有试图处理极度被诅咒的情况,例如a map with object properties。
我的答案的测试用例,它演示了一些边缘情况
const originalValue = [
new Map([['a', {
b: {
_meta: { __meta: "cat" },
c: new Map([['d', 'text']])
}
}]]),
{ _meta: { type: "map" }}
];
console.log(originalValue);
let text = JSON.stringify(originalValue, stringifyReplacer);
console.log(text);
console.log(JSON.parse(text, parseReviver));
接受的答案不是往返
接受的答案真的很可爱。但是,当将具有 dataType 属性的对象传递给它时,它不会往返。
// Test case for the accepted answer
const originalValue = { dataType: "Map" };
const str = JSON.stringify(originalValue, replacer);
const newValue = JSON.parse(str, reviver);
console.log(originalValue, str, newValue);
// > Object { dataType: "Map" } , Map(0)
// Notice how the input was changed into something different