A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://stackoverflow.com/q/29085197/ below:

javascript - How do you JSON.stringify an ES6 Map?

Correctly round-tripping serialization

Just copy this and use it. Or use the npm package.

const serialize = (value) => JSON.stringify(value, stringifyReplacer);
const deserialize = (text) => JSON.parse(text, parseReviver);

// License: CC0
function stringifyReplacer(key, value) {
  if (typeof value === "object" && value !== null) {
    if (value instanceof Map) {
      return {
        _meta: { type: "map" },
        value: Array.from(value.entries()),
      };
    } else if (value instanceof Set) { // bonus feature!
      return {
        _meta: { type: "set" },
        value: Array.from(value.values()),
      };
    } else if ("_meta" in value) {
      // Escape "_meta" properties
      return {
        ...value,
        _meta: {
          type: "escaped-meta",
          value: value["_meta"],
        },
      };
    }
  }
  return value;
}

function parseReviver(key, value) {
  if (typeof value === "object" && value !== null) {
    if ("_meta" in value) {
      if (value._meta.type === "map") {
        return new Map(value.value);
      } else if (value._meta.type === "set") {
        return new Set(value.value);
      } else if (value._meta.type === "escaped-meta") {
        // Un-escape the "_meta" property
        return {
          ...value,
          _meta: value._meta.value,
        };
      } else {
        console.warn("Unexpected meta", value._meta);
      }
    }
  }
  return value;
}
Performance?

There is a version that is equally high quality, but has better performance (tested in Chrome and Firefox). If that matters to you, then please check it out!

https://stackoverflow.com/a/79016027/3492994

Why is this hard?

It should be possible to input any kind of data, get valid JSON, and from there correctly reconstruct the input.

This means dealing with

and on top of those difficulties, the serialisation format must be unambiguous. Otherwise one cannot always reconstruct the input. The top answer has one failing test case, see below.

Hence, I wrote this improved version. It uses _meta instead of dataType, to make conflicts rarer and if a conflict does happen, it actually unambiguously handles it. Hopefully the code is also simple enough to easily be extended to handle other containers.

My answer does, however, not attempt to handle exceedingly cursed cases, such as a map with object properties.

A test case for my answer, which demonstrates a few edge cases

const originalValue = [
  new Map([['a', {
    b: {
      _meta: { __meta: "cat" },
      c: new Map([['d', 'text']])
    }
  }]]),
 { _meta: { type: "map" }}
];

console.log(originalValue);
let text = JSON.stringify(originalValue, stringifyReplacer);
console.log(text);
console.log(JSON.parse(text, parseReviver));
Accepted answer not round-tripping

The accepted answer is really lovely. However, it does not round trip when an object with a dataType property is passed it it. That can make it dangerous to use in certain circumstances, such as

  1. JSON.stringify(data, acceptedAnswerReplacer) and send it over the network.
  2. Naive network handler automatically JSON decodes it. From this point on forth, you cannot safely use the accepted answer with the decoded data, since doing so would cause lots of sneaky issues.

This answer uses a slightly more complex scheme to fix such issues.

// Test case for the accepted answer
const originalValue = { dataType: "Map" };
const str = JSON.stringify(originalValue, replacer);
const newValue = JSON.parse(str, reviver);
console.log(originalValue, str, newValue); 
// > Object { dataType: "Map" } , Map(0)
// Notice how the input was changed into something different

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4