我正在开发一个 Angular 应用程序。我使用 Typescript 并且有一个包含内置 Set 和 Map 类型的特定类。我的问题是我想将我的类的实例作为 JSON 存储在浏览器的 localStorage 中,但是当我尝试将它们字符串化时,我得到这些类型的空字符串。
来自 chrom 控制台的示例:
> inst = new Object({
'elem': new Set(),
'str': 'somestring'
})
: Object {elem: Set(0), str: "somestring"}elem: Set(0)str: "somestring"}
> inst.elem.add('123');
: Set(1) {"123"}
> inst.elem.add('123');
: Set(1) {"123"}size: (...)__proto__: Set[[Entries]]: Array(1)0: "123"length: 1
> JSON.stringify(inst)
: "{"elem":{},"str":"somestring"}"
我唯一能想到的就是在字符串化之前将 Sets 和 Maps 分别递归地转换为 Arrays 和 Objects。但这听起来很费力。
还有更好的办法吗?谢谢。
将 Map 转换为简单的 Object 是行不通的,除非键恰好都是字符串。请记住,Map 可以将任何内容作为键,包括通过引用不相等但在强制转换时会产生相同字符串的内容。
序列化 Maps 和 Sets 的明显方法确实是将它们转换为数组,然后序列化它。幸运的是,您不必自己编写太多代码,只需使用内置的迭代器即可:
const map = new Map([['a', 1], ['b', 2]])
const set = new Set([2,3,5,7,11])
serializedMap = JSON.stringify([...map]) // => [["a", 1], ["b", 2]]
serializedSet = JSON.stringify([...set]) // => [2,3,5,7,11]
现在,正如您所注意到的,嵌套的映射和集合会遇到一些麻烦。为避免编写特定代码来深入研究对象并将其深层嵌套的 Map 和 Set 值转换为数组,您可以在 Map 和 Set 原型(prototype)上定义 toJSON
方法。方法是called implicitly by JSON.stringify :
Map.prototype.toJSON = function () {
return [...this]
}
// or, if you really want to use objects:
Map.prototype.toJSON = function () {
var obj = {}
for(let [key, value] of this)
obj[key] = value
return obj
}
// and for Sets:
Set.prototype.toJSON = function () {
return [...this]
}
请注意,toJSON
可能会在将来的某个时候为 Sets 和 Maps 定义,其行为与此不同。虽然it seems unlikely .
现在,每当您调用 JSON.stringify
并且它看到 Map 或 Set 时,它都会注意到 toJSON
方法并使用它,而不是仅仅复制对象的属性。
另一个类似且更受 ECMA 认可的解决方案是使用 JSON.stringify
的第二个参数。定义一个将预处理值的辅助函数,用适当的数组或对象替换映射和集合:
function mapReplacer(key, value) {
if(value instanceof Map || value instanceof Set) {
return [...value]
// of course you can separate cases to turn Maps into objects
}
return value
}
现在只需将 mapReplacer
传递给 JSON.stringify:
JSON.stringify(map, mapReplacer)