Як JSON.stringify ES6 Map?


105

Я хотів би почати використовувати ES6 Map замість об'єктів JS, але мене стримують, тому що я не можу зрозуміти, як JSON.stringify () Map. Мої ключі гарантовано будуть рядками, і мої значення завжди будуть у списку. Чи справді мені потрібно писати метод обгортки для серіалізації?


1
цікава стаття на тему 2ality.com/2015/08/es6-map-json.html
Девід Чейз

Я зміг змусити це працювати. Результати на Plunkr за адресою embed.plnkr.co/oNlQQBDyJUiIQlgWUPVP . Рішення використовує JSON.stringify (obj, replacecerFunction), який перевіряє, чи передається об'єкт Map, і перетворює об'єкт Map в об'єкт Javascript (що JSON.stringify) перетворить на рядок.
PatS

1
Якщо ваші ключі гарантовано будуть рядками (або числами), а ваші масиви значень , ви можете зробити щось на зразок [...someMap.entries()].join(';'); для чогось більш складного ви можете спробувати щось подібне, використовуючи щось на зразок[...someMap.entries()].reduce((acc, cur) => acc + `${cur[0]}:${/* do something to stringify cur[1] */ }`, '')
user56reinstatemonica8

@Oriol Що, якщо можливо, щоб ім'я ключа було таким, як властивості за замовчуванням? obj[key]може отримати щось несподіване. Розглянемо справу if (!obj[key]) obj[key] = newList; else obj[key].mergeWith(newList);.
Франклін Ю.

Відповіді:


65

Ви не можете.

Ключі карти можуть бути будь-якими, включаючи об'єкти. Але синтаксис JSON дозволяє лише рядки як ключі. Тож у загальному випадку це неможливо.

Мої ключі гарантовано будуть рядками, а мої значення завжди будуть списками

У цьому випадку ви можете використовувати звичайний об'єкт. Він матиме такі переваги:

  • Його можна буде розшифрувати до JSON.
  • Це буде працювати на старих браузерах.
  • Це може бути швидше.

31
для цікавих - в останньому хромі, будь-яка карта серіалізується на "{}"
Капай

Я тут пояснив, що саме я мав на увазі, коли сказав "ти не можеш".
Oriol

7
"Це може бути швидше" - Чи є у вас джерело про це? Я думаю, проста хеш-карта повинна бути швидшою, ніж повномасштабний об'єкт, але я не маю доказів. :)
Ліллеман

1
@Xplouder Цей тест використовує дороге hasOwnProperty. Без цього Firefox повторює об’єкти набагато швидше, ніж карти. Однак карти все одно швидші в Chrome. jsperf.com/es6-map-vs-object-properties/95
Оріоль

1
Правда, здається, що Firefox 45v перебирає об'єкти швидше, ніж Chrome + 49v. Однак Maps все ще виграє проти об'єктів у Chrome.
Xplouder

71

Ви не можете безпосередньо розрядити Mapекземпляр, оскільки він не має жодних властивостей, але ви можете перетворити його на масив кортежів:

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

Для зворотного використовуйте

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

6
Це не перетворюється на об'єкт JSON, а натомість на масив масивів. Не те саме. Дивіться відповідь Евана Керролла нижче, щоб отримати більш повну відповідь.
Сб

3
@SatThiru Масив кортежів - це звичне представлення Maps, воно добре поєднується з конструктором та ітератором. Крім того, це єдине розумне представлення карт, які мають нестрокові ключі, і об'єкт там не працюватиме.
Берги

Бергі, зверніть увагу, що OP сказав: "Мої клавіші гарантовано будуть рядками".
Сб Тіру,

4
@SatThiru В цьому випадку, використання JSON.stringify(Object.fromEntries(map.entries()))іnew Map(Object.entries(JSON.parse(jsonText)))
Берги

@Bergi Stringify не працює, якщо keyце об’єкт, наприклад "{"[object Object]":{"b":2}}"- ключі об’єктів є однією з основних особливостей Карт
Дренай

59

Обидва JSON.stringifyі JSON.parseпідтримують другий аргумент. replacerі reviverвідповідно. За допомогою замінника та відроджувача нижче можна додати підтримку власного об’єкта Map, включаючи глибоко вкладені значення

function replacer(key, value) {
  const originalObject = this[key];
  if(originalObject instanceof Map) {
    return {
      dataType: 'Map',
      value: Array.from(originalObject.entries()), // or with spread: value: [...originalObject]
    };
  } 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);


1
Безумовно, найкраща відповідь тут.
JavaRunner

15

Хоча ще немає методу, передбаченого 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"]]'


6

Stringify Mapекземпляра (об'єкти як ключі в порядку) :

JSON.stringify([...map])

або

JSON.stringify(Array.from(map))

або

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

вихідний формат:

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

4

Краще рішення

    // 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
        }
    }

Сподіваюся, це менш кривдно, ніж відповіді вище.


Я не впевнений, що багато хто буде задоволений розширенням класу основної карти просто для серіалізації до json ...
vasia

1
Вони не повинні бути, але це більш ТВЕРДИЙ спосіб зробити це. Зокрема, це узгоджується з принципами LSP та OCP SOLID. Тобто, рідна Карта розширюється, а не модифікується, і все ще можна використовувати заміну Ліскова (LSP) з рідною картою. Звичайно, це більше ООП, ніж багато початківців або впевнених людей у ​​функціональному програмуванні, але, принаймні, це залежать від перевіреної та справжньої базової лінії основних принципів проектування програмного забезпечення. Якщо ви хочете застосувати принцип розділення інтерфейсу (ISP) SOLID, ви можете мати невеликий IJSONAbleінтерфейс (звичайно, використовуючи TypeScript).
Коді,

3

Наведене нижче рішення працює, навіть якщо у вас є вкладені Карти

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);
}

Не перевіряючи вашу відповідь, я вже ціную, як він привертає увагу до проблеми вкладених карт. Навіть якщо ви успішно перетворите це на JSON, будь-який аналіз, зроблений у майбутньому, повинен чітко усвідомлювати, що JSON спочатку був Mapі (що ще гірше), що кожна підкарта (вона містить) також була спочатку картою. В іншому випадку неможливо переконатися, що array of pairsне просто призначена саме така, а замість карти. Ієрархії об’єктів та масивів не несуть цього тягаря при розборі. Будь-яка правильна серіалізація Mapявно вказує на те, що вона є Map.
Лонні Бест

Детальніше про це тут .
Лонні Бест
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.