我想开始使用ES6 Map而不是JS对象,但我受到阻碍,因为我不知道如何对Map
进行JSON.stringify()
。 我的键保证是字符串,值将始终被列出。 我真的必须编写一个包装器方法来序列化吗?
我想开始使用ES6 Map而不是JS对象,但我受到阻碍,因为我不知道如何对Map
进行JSON.stringify()
。 我的键保证是字符串,值将始终被列出。 我真的必须编写一个包装器方法来序列化吗?
JSON.stringify
和JSON.parse
都支持第二个参数,分别为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);
dataType
在网络传输中"弄脏"数据,但我想不到更好的方法了。谢谢。 - rynopthis[key]
而不是value
? - JimiDiniconst getUrl = (url) => fetch(url, apiOptions).then(res => res.json())
,其中 apiOptions
有一些全局设置。值得记住的是,这种情况可能会发生,必须正确地进行适应。 - Pawel您无法直接将Map
实例字符串化,因为它没有任何属性,但是您可以将其转换为元组数组:
jsonText = JSON.stringify(Array.from(map.entries()));
要进行反向操作,请使用
map = new Map(JSON.parse(jsonText));
JSON.stringify(Object.fromEntries(map.entries()))
和new Map(Object.entries(JSON.parse(jsonText)))
。 - Bergikey
是一个对象,例如 "{"[object Object]":{"b":2}}"
,则 Stringify 将无法工作 - 对象键是 Maps 的主要特征之一。 - DrenaiObject.fromEntries
,而是使用我主要答案中的代码,而不是评论中的代码。构建对象字面量的代码是为了响应Sat Thiru的情况,其中键是字符串。 - Bergi无法实现。
一个Map对象的键可以是任何类型,包括对象。但是在JSON语法中,键只能是字符串。因此一般情况下是不可能实现的。
我的键保证是字符串,值将始终是列表
在这种情况下,您可以使用普通对象。它将具有以下优势:
hasOwnProperty
。没有它,Firefox在迭代对象时比映射要快得多。不过,在Chrome上,映射仍然更快。http://jsperf.com/es6-map-vs-object-properties/95 - OriolJSON.stringify
将Map
映射为JavaScript原始类型来实现此功能。以下是我们将使用的示例Map
。const map = new Map();
map.set('foo', 'bar');
map.set('baz', 'quz');
您可以使用以下辅助函数将其转换为 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"}'
这个问题的辅助函数会更加简洁
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"]]'
__proto__
等键的代码吗?否则,尝试序列化这样的映射可能会损坏整个环境。我相信 Alok 的回答没有这个问题。 - roim使用扩展语法可以将Map序列化为一行:
JSON.stringify([...new Map()]);
使用以下方法进行反序列化:
let map = new Map(JSON.parse(map));
考虑到你的示例是一个简单的用例,其中键将是简单类型,我认为这是将Map转换为JSON字符串最简单的方法。
JSON.stringify(Object.fromEntries(map));
我对 Map 的底层数据结构的想法是将其看作一组键值对数组(本身也是数组)。因此,类似于这样:
const myMap = new Map([
["key1", "value1"],
["key2", "value2"],
["key3", "value3"]
]);
由于底层数据结构与Object.entries相同,我们可以像对待数组一样在Map上使用本地JavaScript方法 Object.fromEntries()
:
Object.fromEntries(myMap);
/*
{
key1: "value1",
key2: "value2",
key3: "value3"
}
*/
然后你只需要对其结果使用JSON.stringify()。
Object.fromEntries()
是非破坏性的,因此您仍将保留原始的 Map。 - Alok SomaniMap<string, otherType>
。 - Laercio Metzner // 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
}
}
IJSONAble
接口(当然要使用TypeScript)。 - Cody只需复制并使用它,或者使用npm包。
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;
}
希望能够输入任何类型的数据,得到有效的JSON,并从中正确地重构输入。
这意味着需要处理以下情况:
new Map([ [{cat:1}, "value"] ])
。这意味着使用Object.fromEntries
的任何答案可能都是错误的。new Map([ ["key", new Map([ ["nested key", "nested value"] ])] ])
。许多答案通过仅回答问题而不涉及任何其他内容来回避此问题。{"key": new Map([ ["nested key", "nested value"] ]) }
。除了这些困难之外,序列化格式必须是明确的。否则,不一定总是能够重新构建输入。最佳答案有一个失败的测试用例,请参见下文。
因此,我编写了这个改进版本。它使用_meta
而不是dataType
,使冲突更少,如果发生冲突,它实际上可以无歧义地处理它。希望代码也足够简单,可以轻松地扩展到处理其他容器。
然而,我的答案不尝试处理极端恶劣的情况,例如具有对象属性的映射。
下面是我的答案的一个测试用例,演示了一些边缘情况:
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));
这个被接受的答案非常好。然而,当传递一个带有 dataType
属性的对象时,它无法回传。在某些情况下,这可能会使其使用变得危险,例如:
JSON.stringify(data, acceptedAnswerReplacer)
并通过网络发送。此答案使用了更复杂的方案来解决这些问题。
// 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
将Map
实例转换为字符串(键为对象也可以):
JSON.stringify([...map])
或者JSON.stringify(Array.from(map))
或者JSON.stringify(Array.from(map.entries()))
输出格式:
// [["key1","value1"],["key2","value2"]]
const map = new Map();
map.set('Key1', "Value1");
map.set('Key2', "Value2");
console.log(Object.fromEntries(map));
{"键1": "值1","键2": "值2"}
JSON.stringify(Object.fromEntries(new Map([['s', 'r'],[{s:3},'g']])))
的结果会变成 '{"s":"r","[object Object]":"g"}'
。 - asp47
[...someMap.entries()].join(';')
; 对于更复杂的情况,你可以尝试类似的方法,使用[...someMap.entries()].reduce((acc, cur) => acc + \
${cur[0]}:${/* do something to stringify cur[1] */ }`, '')`。 - user56reinstatemonica8obj[key]
可能会得到一些意外的结果。考虑这种情况:if (!obj[key]) obj[key] = newList; else obj[key].mergeWith(newList);
。 - Franklin Yu