Intro: Let's follow the convention of starting class names with an uppercase letter, therefore changing test
to Test
below.
As Map
cannot be stringified by default, there are three options available:
Add a toJSON
method to your Test
class and return an object with modified children
(likely an array of arrays), or
Create a subclass of Map
that includes toJSON
and utilize it in Test
Develop a replacer for use with JSON.stringify
that handles instances of Map
.
While option #1 is functional, it would require updates to the toJSON
method whenever properties are added or removed from Test
, potentially leading to maintenance challenges:
class Test {
name: string
children: Map<string, string> = new Map()
constructor() {
this.name = 'KIANA'
this.children.set('name', 'OTTO')
}
toJSON() {
return {
name: this.name,
children: [...this.children.entries()]
}
}
}
var t = new Test()
console.log(JSON.stringify(t))
Here's a demonstration:
class Test {
name/*: string*/
children/*: Map<string, string>*/ = new Map()
constructor() {
this.name = 'KIANA'
this.children.set('name', 'OTTO')
}
toJSON() {
return {
name: this.name,
children: [...this.children.entries()]
}
}
}
var t = new Test()
console.log(JSON.stringify(t))
[...this.children.entries()]
generates an array of [name, value]
pairs for the map.
Personally, I prefer option #2, a JSON-compatible Map
:
class JSONAbleMap extends Map {
toJSON() {
return [...this.entries()]
}
}
...which you can then apply in Test
:
class Test {
name: string
children: Map<string, string> = new JSONAbleMap()
constructor() {
this.name = 'KIANA'
this.children.set('name', 'OTTO')
}
}
var t = new Test()
console.log(JSON.stringify(t))
Example of usage:
class JSONAbleMap extends Map {
toJSON() {
return [...this.entries()]
}
}
class Test {
name/*: string*/
children/*: Map<string, string>*/ = new JSONAbleMap()
constructor() {
this.name = 'KIANA'
this.children.set('name', 'OTTO')
}
}
var t = new Test()
console.log(JSON.stringify(t))
Alternatively, opt for choice #3, a replacer function applied with JSON.stringify
:
function mapAwareReplacer(key: string|Symbol, value: any): any {
if (value instanceof Map && typeof value.toJSON !== "function") {
return [...value.entries()]
}
return value
}
...and incorporate it when using JSON.stringify
:
console.log(JSON.stringify(t, mapAwareReplacer))
Live demo:
function mapAwareReplacer(key, value) {
if (value instanceof Map && typeof value.toJSON !== "function") {
return [...value.entries()]
}
return value
}
class Test {
name/*: string*/
children/*: Map<string, string>*/ = new Map()
constructor() {
this.name = 'KIANA'
this.children.set('name', 'OTTO')
}
}
var t = new Test()
console.log(JSON.stringify(t, mapAwareReplacer))