Как сохранить карту ES6 в localstorage (или в другом месте)?
var a = new Map([[ 'a', 1 ]]);
a.get('a') // 1
var forStorageSomewhere = JSON.stringify(a);
// Store, in my case, in localStorage.
// Later:
var a = JSON.parse(forStorageSomewhere);
a.get('a') // TypeError: undefined is not a function
к сожалению JSON.stringify(a); просто возвращает ' {}', что означает, что a становится пустым объектом при восстановлении.
нашел es6-mapify это позволяет вверх / вниз-кастинг между картой и простым объектом, так что это может быть одно решение, но я надеялся, что мне нужно будет прибегнуть к внешней зависимости просто сохранить мою карту.
7 ответов:
предполагая, что и ваши ключи, и ваши значения сериализуемы,
localStorage.myMap = JSON.stringify(Array.from(map.entries()));должны работать. Для обратного используйте
map = new Map(JSON.parse(localStorage.myMap));
обычно сериализация полезна только в том случае, если это свойство содержит
deserialize(serialize(data)).get(key) ≈ data.get(key)здесь
a ≈ bможно определить какserialize(a) === serialize(b).это выполняется при сериализации объекта в JSON:
var obj1 = {foo: [1,2]}, obj2 = JSON.parse(JSON.stringify(obj1)); obj1.foo; // [1,2] obj2.foo; // [1,2] :) JSON.stringify(obj1.foo) === JSON.stringify(obj2.foo); // true :)и это работает, потому что свойства могут быть только строками, которые могут быть без потерь сериализованы в строки.
однако карты ES6 допускают произвольные значения в качестве ключей. Это проблематично, потому что объекты однозначно идентифицируются по их ссылке, а не их данные. И при сериализации объектов вы теряете ссылки.
var key = {}, map1 = new Map([ [1,2], [key,3] ]), map2 = new Map(JSON.parse(JSON.stringify([...map1.entries()]))); map1.get(1); // 2 map2.get(1); // 2 :) map1.get(key); // 3 map2.get(key); // undefined :(так я бы сказал вообще это невозможно чтобы сделать это полезным способом.
и для тех случаев, когда она будет работать, скорее всего вы можете использовать простой объект вместо карты. Это также будет иметь следующие преимущества:
- он сможет быть привязан к JSON без потери ключевой информации.
- он будет работать на старых броузеры.
- это может быть быстрее.
строительство - ля -ответ, мы можем сделать немного лучше. Мы все еще можем использовать ссылки на объекты для ключей, пока есть примитивный корень или вход в карту, и каждый ключ объекта может быть транзитивно найден из этого корневого ключа.
изменение примера Ориола для использования Дугласа Крокфорда JSON.decycle и JSON.ретроцикл мы можем создать карту, которая обрабатывает этот случай:
var key = {}, map1 = new Map([ [1, key], [key, 3] ]), map2 = new Map(JSON.parse(JSON.stringify([...map1.entries()]))), map3 = new Map(JSON.retrocycle(JSON.parse(JSON.stringify(JSON.decycle([...map1.entries()]))))); map1.get(1); // key map2.get(1); // key map3.get(1); // key map1.get(map1.get(1)); // 3 :) map2.get(map2.get(1)); // undefined :( map3.get(map3.get(1)); // 3 :)Decycle и retrocycle делают его можно кодировать циклических структур и групп обеспечения доступности баз данных в JSON. Это полезно, если мы хотим построить отношения между объектами без создания дополнительных свойств на самих этих объектах или хотим взаимозаменяемо связать примитивы с объектами и наоборот, используя карту ES6.
одна ловушка в том, что мы не может используйте исходный ключевой объект для новой карты (
map3.get(key);вернет undefined). Однако, удерживая исходную ссылку на ключ, но недавно проанализированный JSON карта кажется очень маловероятным случаем, чтобы когда-либо иметь.
принятый ответ не будет выполнен, когда у вас есть многомерные карты. Следует всегда иметь в виду, что объект карты может принимать другой объект карты в качестве ключа или значения.
таким образом, лучший и безопасный способ обработки этой работы может быть следующим;
function arrayifyMap(m){ return m.constructor === Map ? [...m].map(([v,k]) => [arrayifyMap(v),arrayifyMap(k)]) : m; }Если у вас есть этот инструмент, то вы можете всегда нравится;
localStorage.myMap = JSON.stringify(arrayifyMap(myMap))
одна вещь, которая остается вне этой карты-это приказал структура-т. е. при итерации первый введенный элемент будет первым в списке.
Это не как объект Javascript. Мне потребовался этот тип структуры (поэтому я использовал Map), а затем, чтобы узнать, что JSON.stringify не работает болезненно (но понятно).
Я закончил тем, что сделал функцию 'value_to_json', что означает разбор всего - используя JSON.преобразовать в строки только для самых основных "типов".
к сожалению, подкласс карты с a .toJSON () не работает, так как он исключает значение не JSON_string. И считается наследием.
мой вариант использования был бы исключительным, хотя.
по теме:
- https://github.com/DavidBruant/Map-Set.prototype.toJSON/issues/16
- JSON оставил бесконечность и NaN; статус JSON в ECMAScript?
- как чтобы структурировать объекты, содержащие наборы и карты ES5?
- JSON stringify a Set
function value_to_json(value) { if (value === null) { return 'null'; } if (value === undefined) { return 'null'; } //DEAL WITH +/- INF at your leisure - null instead.. const type = typeof value; //handle as much as possible taht have no side effects. function could //return some MAP / SET -> TODO, but not likely if (['string', 'boolean', 'number', 'function'].includes(type)) { return JSON.stringify(value) } else if (Object.prototype.toString.call(value) === '[object Object]') { let parts = []; for (let key in value) { if (Object.prototype.hasOwnProperty.call(value, key)) { parts.push(JSON.stringify(key) + ': ' + value_to_json(value[key])); } } return '{' + parts.join(',') + '}'; } else if (value instanceof Map) { let parts_in_order = []; value.forEach((entry, key) => { if (typeof key === 'string') { parts_in_order.push(JSON.stringify(key) + ':' + value_to_json(entry)); } else { console.log('Non String KEYS in MAP not directly supported'); } //FOR OTHER KEY TYPES ADD CUSTOM... 'Key' encoding... }); return '{' + parts_in_order.join(',') + '}'; } else if (typeof value[Symbol.iterator] !== "undefined") { //Other iterables like SET (also in ORDER) let parts = []; for (let entry of value) { parts.push(value_to_json(entry)) } return '[' + parts.join(',') + ']'; } else { return JSON.stringify(value) } } let m = new Map(); m.set('first', 'first_value'); m.set('second', 'second_value'); let m2 = new Map(); m2.set('nested', 'nested_value'); m.set('sub_map', m2); let map_in_array = new Map(); map_in_array.set('key', 'value'); let set1 = new Set(["1", 2, 3.0, 4]); m2.set('array_here', [map_in_array, "Hello", true, 0.1, null, undefined, Number.POSITIVE_INFINITY, { "a": 4 }]); m2.set('a set: ', set1); const test = { "hello": "ok", "map": m }; console.log(value_to_json(test));
если вы реализуете свой собственный
toJSON()функции для любогоclassобъекты у вас тогда обычный старыйJSON.stringify()будет просто работать!
MapСArrays для ключей?MapС другойMapкак ценности? АMapдомаObject? Может быть, даже свой собственный класс; легко.Map.prototype.toJSON = function() { return Array.from(this.entries()); };вот именно! здесь требуется манипуляция прототипом. Вы можете пойти вокруг добавления
toJSON()вручную для всех ваших нестандартные вещи, но на самом деле вы просто избегаете власти JS
test = { regular : 'object', map : new Map([ [['array', 'key'], 7], ['stringKey' , new Map([ ['innerMap' , 'supported'], ['anotherValue', 8] ])] ]) }; console.log(JSON.stringify(test));выходы:
{"regular":"object","map":[[["array","key"],7],["stringKey",[["innerMap","supported"],["anotherValue",8]]]]}
десериализация не является автоматической, но так же просто, используя приведенную выше результирующую строку, я переделаю Карты, чтобы вытащить значение:
test2 = JSON.parse(JSON.stringify(test)); console.log((new Map((new Map(test2.map)).get('stringKey'))).get('innerMap'));очевидно, что вы не сделаете это в одной строке и не пренебрегаете сохранением промежуточных карт, это просто докажет, что все данные там, чтобы восстановить оригинал, чтобы использовать
get()"supported"
Comments