你如何 JSON.stringify ES6 地图?

How do you JSON.stringify an ES6 Map?

我想开始使用 ES6 Map 而不是 JS 对象,但我被阻止了,因为我不知道如何 JSON.stringify() a Map。我的键保证是字符串,我的值将始终列出。我真的必须编写一个包装器方法来序列化吗?

你不能。

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

My keys are guaranteed to be strings and my values will always be lists

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

  • 可以字符串化为JSON.
  • 它将在旧版浏览器上运行。
  • 它可能会更快。

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

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

相反,使用

map = new Map(JSON.parse(jsonText));

虽然 ecmascript 还没有提供任何方法,但如果您将 Map 映射到 JavaScript 原语,仍然可以使用 JSON.stingify 来完成。这是我们将使用的示例 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"]]'

JSON.stringifyJSON.parse都支持第二个参数。 replacerreviver 分别。使用下面的 replacer 和 reviver,可以添加对原生 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);

即使您有嵌套地图,以下解决方案也适用

function stringifyMap(myMap) {
    function selfIterator(map) {
        return Array.from(map).reduce((acc, [key, value]) => {
            if (value instanceof Map) {
                acc[key] = selfIterator(value);
            } else {
                acc[key] = value;
            }

            return acc;
        }, {})
    }

    const res = selfIterator(myMap)
    return JSON.stringify(res);
}

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

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

并反序列化:

let map = new Map(JSON.parse(map));

更好的解决方案

    // somewhere...
    class Klass extends Map {

        toJSON() {
            var object = { };
            for (let [key, value] of this) object[key] = value;
            return object;
        }

    }

    // somewhere else...
    import { Klass as Map } from '@core/utilities/ds/map';  // <--wherever "somewhere" is

    var map = new Map();
    map.set('a', 1);
    map.set('b', { datum: true });
    map.set('c', [ 1,2,3 ]);
    map.set( 'd', new Map([ ['e', true] ]) );

    var json = JSON.stringify(map, null, '\t');
    console.log('>', json);

输出

    > {
        "a": 1,
        "b": {
            "datum": true
        },
        "c": [
            1,
            2,
            3
        ],
        "d": {
            "e": true
        }
    }

希望上面的答案没有那么令人毛骨悚然。

字符串化一个 Map 实例 (对象作为键是可以的):

JSON.stringify([...map])

JSON.stringify(Array.from(map))

JSON.stringify(Array.from(map.entries()))

输出格式:

// [["key1","value1"],["key2","value2"]]

鉴于您的示例是一个简单的用例,其中键将是简单类型,我认为这是 JSON 将 Map 字符串化的最简单方法。

JSON.stringify(Object.fromEntries(map));

我认为 Map 的底层数据结构是 key-value 对的数组(作为数组本身)。所以,像这样:

const myMap = new Map([
     ["key1", "value1"],
     ["key2", "value2"],
     ["key3", "value3"]
]);

因为底层数据结构是我们在 Object.entries 中找到的,我们可以像在数组上一样在 Map 上使用 Object.fromEntries() 的原生 JavaScript 方法:

Object.fromEntries(myMap);

/*
{
     key1: "value1",
     key2: "value2",
     key3: "value3"
}
*/

然后您剩下的就是对结果使用 JSON.stringify()。

只想分享我的 Map 和 Set 版本 JSON.stringify。 我正在整理它们,对调试很有用...

function replacer(key, value) {
    if (value instanceof Map) {
        const reducer = (obj, mapKey) => {
            obj[mapKey] = value.get(mapKey);
            return obj;
        };
        return [...value.keys()].sort().reduce(reducer, {});
    } else if (value instanceof Set) {
        return [...value].sort();
    }
    return value;
}

用法:

const map = new Map();
const numbers= new Set()
numbers.add(3);
numbers.add(2);
numbers.add(3);
numbers.add(1);
const chars= new Set()
chars.add('b')
chars.add('a')
chars.add('a')
map.set("numbers",numbers)
map.set("chars",chars)

console.log(JSON.stringify(map, replacer, 2));

结果:

{
  "chars": [
    "a",
    "b"
  ],
  "numbers": [
    1,
    2,
    3
  ]
}

虽然在某些情况下,如果您是地图的创建者,您会在单独的 'src' 文件中编写代码并将副本另存为 .txt 文件,如果编写得足够简洁,可以很容易地读入、解密并添加到服务器端。

然后新文件将保存为 .js 文件,并从服务器发回对它的引用。一旦以 JS 形式读回,该文件将完美地重建自身。美妙之处在于重建不需要骇人听闻的迭代或解析。

非常简单的方法。

  const map = new Map();
  map.set('Key1', "Value1");
  map.set('Key2', "Value2");
  console.log(Object.fromEntries(map));

` 输出:-

{"Key1": "Value1","Key2": "Value2"}