你如何 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.stringify
和JSON.parse
都支持第二个参数。 replacer
和 reviver
分别。使用下面的 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"}
我想开始使用 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.stringify
和JSON.parse
都支持第二个参数。 replacer
和 reviver
分别。使用下面的 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"}