你如何 JSON.stringify 一个 ES6 Map?

IT技术 javascript json dictionary ecmascript-6
2021-01-15 18:27:50

我想开始使用ES6地图,而不是JS对象,但我踌躇,因为我无法弄清楚如何JSON.stringify()一个Map我的键保证是字符串,我的值将始终被列出。我真的必须编写一个包装方法来序列化吗?

6个回答

双方JSON.stringifyJSON.parse支持第二个参数。replacerreviver分别。使用下面的替换器和恢复器,可以添加对原生 Map 对象的支持,包括深度嵌套的值

function replacer(key, value) {
  if(value instanceof Map) {
    return {
      dataType: 'Map',
      value: Array.from(value.entries()), // or with spread: value: [...value]
    };
  } else {
    return value;
  }
}
function reviver(key, value) {
  if(typeof value === 'object' && value !== null) {
    if (value.dataType === 'Map') {
      return new Map(value.value);
    }
  }
  return value;
}

用法

const originalValue = new Map([['a', 1]]);
const str = JSON.stringify(originalValue, replacer);
const newValue = JSON.parse(str, reviver);
console.log(originalValue, newValue);

结合数组、对象和地图的深度嵌套

const originalValue = [
  new Map([['a', {
    b: {
      c: new Map([['d', 'text']])
    }
  }]])
];
const str = JSON.stringify(originalValue, replacer);
const newValue = JSON.parse(str, reviver);
console.log(originalValue, newValue);
@Pawel 使用this[key]而不是的原因value什么?
2021-03-15 18:27:50
只是将其标记为正确。虽然我不喜欢你必须用非标准化的dataType. 谢谢。
2021-03-22 18:27:50
对我来说似乎有一个小问题:任何偶然具有属性 o.dataType==='Map' 的普通对象 o 在序列化-反序列化时也将被转换为 Map 。
2021-03-27 18:27:50
@JimiDini 好点,已更新。现在,如果有人想将这些声明为箭头函数,它不会弄乱作用域
2021-03-29 18:27:50
@rynop 是的,它更像是一个拥有自己数据存储格式的小程序,而不仅仅是使用纯本机功能
2021-04-08 18:27:50

您不能直接对Map实例进行字符串化,因为它没有任何属性,但您可以将其转换为元组数组:

jsonText = JSON.stringify(Array.from(map.entries()));

对于相反的情况,请使用

map = new Map(JSON.parse(jsonText));
@SatThiru 元组数组是Maps的习惯表示,它与构造函数和迭代器配合得很好。它也是具有非字符串键的映射的唯一合理表示,并且对象在那里不起作用。
2021-03-11 18:27:50
@Drenai 然后不要使用Obect.fromEntries, 并使用我的主要答案中的代码而不是评论中的代码。构建对象字面量的代码是对 Sat Thiru 的回应,他给出了键是字符串的情况。
2021-03-19 18:27:50
这不会转换为 JSON 对象,而是转换为数组的 Array。不是一回事。有关更完整的答案,请参阅下面的 Evan Carroll 的答案。
2021-04-06 18:27:50
Bergi,请注意 OP 说“我的密钥保证是字符串”。
2021-04-07 18:27:50

你不能。

地图的键可以是任何东西,包括对象。但是 JSON 语法只允许字符串作为键。所以在一般情况下是不可能的。

我的键保证是字符串,我的值将始终是列表

在这种情况下,您可以使用普通对象。它将具有以下优点:

  • 它将能够被字符串化为 JSON。
  • 它适用于较旧的浏览器。
  • 它可能会更快。
对于最新版 chrome 的好奇,任何地图都会序列化为“{}”
2021-03-15 18:27:50
确实,Firefox 45v 似乎比 Chrome +49v 迭代对象更快。然而,地图在 Chrome 中仍然胜过对象。
2021-03-28 18:27:50
@Xplouder 该测试使用了昂贵的hasOwnProperty. 没有它,Firefox 迭代对象的速度比地图快得多。不过,Chrome 上的地图仍然更快。jsperf.com/es6-map-vs-object-properties/95
2021-03-29 18:27:50
“它可能会更快” - 你有任何消息来源吗?我想象一个简单的哈希映射必须比一个完整的对象更快,但我没有证据。:)
2021-03-31 18:27:50
由于这一点,只是路过并找出我的问题。有时候,我真的很想搬到一个农场,把这一切都抛在脑后。
2021-04-07 18:27:50

虽然 ecmascript 还没有提供任何方法,但JSON.stingify如果您将 映射Map到 JavaScript 原语,仍然可以使用来完成这是Map我们将使用的示例

const map = new Map();
map.set('foo', 'bar');
map.set('baz', 'quz');

转到 JavaScript 对象

您可以使用以下辅助函数转换为 JavaScript 对象字面量。

const mapToObj = m => {
  return Array.from(m).reduce((obj, [key, value]) => {
    obj[key] = value;
    return obj;
  }, {});
};

JSON.stringify(mapToObj(map)); // '{"foo":"bar","baz":"quz"}'

转到 JavaScript 对象数组

这个辅助函数会更紧凑

const mapToAoO = m => {
  return Array.from(m).map( ([k,v]) => {return {[k]:v}} );
};

JSON.stringify(mapToAoO(map)); // '[{"foo":"bar"},{"baz":"quz"}]'

转到数组数组

这更容易,你可以使用

JSON.stringify( Array.from(map) ); // '[["foo","bar"],["baz","quz"]]'
> 转到一个 JavaScript 对象 < 它不应该有代码来处理诸如 之类的键__proto__吗?或者您可以通过尝试序列化这样的地图来破坏整个环境。我相信,Alok 的回应不会因此受到影响。
2021-03-20 18:27:50

使用spread sytax Map 可以在一行中序列化:

JSON.stringify([...new Map()]);

并将其反序列化:

let map = new Map(JSON.parse(map));
这适用于一维地图,但不适用于 n 维地图。
2021-04-04 18:27:50