The existing solution to this question is still correct.
Do you want some kind of drop-in replacement for JSON.stringify()
and JSON.parse()
that works with Map
values? You'd need to write it yourself.
Both functions take an optional parameter (replacer
for JSON.stringify()
and reviver
for JSON.parse()
) that allow you to customize how JavaScript objects are converted to and from JSON. Here's a possibility:
const JSONWithMap = (mapIdentifier: string = "SerializedES6Map!!") => ({
stringify(
value: any,
replacer?: ((key: string, value: any) => any) | (number | string)[] | null,
space?: string | number
): string {
let innerReplacer: (key: string, value: any) => any;
if ((typeof replacer === 'undefined') || (replacer === null)) {
innerReplacer = (k, v) => v;
} else if (Array.isArray(replacer)) {
innerReplacer = (k, v) => replacer.indexOf(k) >= 0 ? v : void 0;
} else {
innerReplacer = replacer;
}
let outerReplacer = function(this: any, key: string, value: any) {
if (value instanceof Map) {
value = { [mapIdentifier]: Array.from(value.entries()) };
}
return innerReplacer.call(this, key, value);
}
return JSON.stringify(value, outerReplacer, space);
},
parse(text: string, reviver?: (key: any, value: any) => any): any {
let innerReviver: (key: string, value: any) => any;
if (typeof reviver === 'undefined' || reviver === null) {
innerReviver = (k, v) => v;
} else {
innerReviver = reviver;
}
let outerReviver = function(this: any, key: string, value: any) {
if ((typeof value !== 'undefined') && (Array.isArray(value[mapIdentifier]))) {
return new Map(value[mapIdentifier]);
}
return innerReviver.call(this, key, value);
}
return JSON.parse(text, outerReviver);
}
});
The function JSONWithMap()
takes an optional string parameter for marking when a Map
is being serialized and deserialized, and it defaults to "SerializedES6Map!!"
which is hopefully something you will not use in other objects. Its stringify()
method acts just like JSON.stringify()
except it also converts Map
s to an object with a single key whose value is the serialized array of entries, and its parse()
method acts just like JSON.parse()
except it recognizes those serialized Map
objects and deserializes them into Map
s.
You can use it like this:
declare const widget: Widget; // some widget instance
const JSONM = JSONWithMap(); // use default identifier
const widgetJSON = JSONM.stringify(widget); // serialize
const deserializedWidget = JSONM.parse(widgetJSON); // deserialize into plain JS object
const rehydratedWidget = Object.assign(new Widget(), deserializedWidget); // actually make instance of class from plain JS object
Note I say "you'd need to write it yourself" and then hand you an implementation. Well the implementation has not been tested except for very briefly, so you need to thoroughly test it, do something custom for your Widget
class that isn't a drop-in replacement for JSON.stringify()
, or write it yourself.
Good luck.