Converting a Map to a simple Object won't work, unless the keys happen to be all strings. Remember that Maps can have anything as keys, including things that are not equal by reference but that will result in the same string when coerced.
The obvious way to serialise Maps and Sets would be, indeed, to convert them to arrays and then serialise that. Fortunately, you don't have to write much code yourself, just use the built-in iterators:
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]
Now, as you noticed, you'll have some trouble with nested Maps and Sets. To avoid writing specific code to dive into objects and turn their deeply nested Map and Set values into arrays, you can define the toJSON
method on Map and Set prototypes. The method is 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]
}
Be careful, toJSON
may be defined for Sets and Maps sometime in the future, with a different behaviour than this. Although it seems unlikely.
Now, whenever you call JSON.stringify
and it sees a Map or Set, it will notice the toJSON
method and use it instead of just copying the object's properties.
Another, similar and more ECMA-approved solution is to use JSON.stringify
's second argument. Define a helper function that will preprocess values, replacing Maps and Sets with appropriate arrays or objects:
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
}
Now just pass mapReplacer
to JSON.stringify:
JSON.stringify(map, mapReplacer)