如何对包含ES5集和地图的对象进行字符串化?

时间:2017-04-30 09:28:47

标签: javascript json html5

我正在开发一个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。但这听起来很费劲。

有更好的方法吗?感谢。

1 个答案:

答案 0 :(得分:8)

将地图转换为简单的对象不会起作用,除非键恰好是所有字符串。请记住,地图可以包含任何键作为键,包括通过引用不相同但在强制时会产生相同字符串的内容。

序列化地图和集合的明显方法实际上是将它们转换为数组,然后将其序列化。幸运的是,您不必自己编写很多代码,只需使用内置迭代器:

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 prototypes上定义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,但行为与此不同。虽然it seems unlikely

现在,每当您致电JSON.stringify并看到地图或设置时,它都会注意到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)