Bagaimana Anda JSON.stringify Peta ES6?

105

Saya ingin mulai menggunakan Peta ES6 daripada objek JS tetapi saya ditahan karena saya tidak tahu cara JSON.stringify () Peta. Kunci saya dijamin berupa string dan nilai saya akan selalu dicantumkan. Apakah saya benar-benar harus menulis metode pembungkus untuk membuat serial?

rynop
sumber
1
artikel menarik tentang topik 2ality.com/2015/08/es6-map-json.html
David Chase
Saya bisa mendapatkan ini untuk bekerja. Hasilnya ada di Plunkr di embed.plnkr.co/oNlQQBDyJUiIQlgWUPVP . Solusinya menggunakan JSON.stringify (obj, replacerFunction) yang memeriksa apakah objek Map sedang diteruskan dan mengubah objek Map menjadi objek Javascript (JSON.stringify itu) kemudian akan diubah menjadi string.
PatS
1
Jika kunci Anda dijamin berupa string (atau angka) dan nilai array Anda, Anda dapat melakukan sesuatu seperti [...someMap.entries()].join(';'); untuk sesuatu yang lebih kompleks, Anda dapat mencoba sesuatu yang serupa menggunakan sesuatu seperti[...someMap.entries()].reduce((acc, cur) => acc + `${cur[0]}:${/* do something to stringify cur[1] */ }`, '')
user56reinstatemonica8
@Oriol Bagaimana jika mungkin nama kunci sama dengan properti default? obj[key]mungkin memberi Anda sesuatu yang tidak terduga. Pertimbangkan kasusnya if (!obj[key]) obj[key] = newList; else obj[key].mergeWith(newList);.
Franklin Yu

Jawaban:

65

Tidak boleh.

Kunci peta bisa apa saja, termasuk objek. Tetapi sintaks JSON hanya mengizinkan string sebagai kunci. Jadi tidak mungkin dalam kasus umum.

Kunci saya dijamin berupa string dan nilai saya akan selalu berupa daftar

Dalam hal ini, Anda dapat menggunakan objek biasa. Ini akan memiliki keuntungan berikut:

  • Ini akan dapat dirangkai menjadi JSON.
  • Ini akan berfungsi di browser lama.
  • Mungkin lebih cepat.
Oriol
sumber
31
bagi yang penasaran dengan chrome terbaru, semua peta berseri menjadi '{}'
Capaj
Saya telah menjelaskan di sini apa sebenarnya yang saya maksud ketika saya berkata "Anda tidak bisa".
Oriol
8
"Mungkin lebih cepat" - Apakah Anda punya sumber tentang itu? Saya membayangkan hash-map sederhana harus lebih cepat daripada objek yang sepenuhnya meledak, tetapi saya tidak punya bukti. :)
Leman
1
@Xplouder Tes itu menggunakan mahal hasOwnProperty. Tanpa itu, Firefox melakukan iterasi objek jauh lebih cepat daripada peta. Maps masih lebih cepat di Chrome. jsperf.com/es6-map-vs-object-properties/95
Oriol
1
Benar, tampaknya Firefox 45v mengiterasi objek jauh lebih cepat daripada Chrome + 49v. Namun Maps masih menang vs objek di Chrome.
Xplouder
71

Anda tidak dapat langsung merangkai Mapinstance karena tidak memiliki properti apa pun, tetapi Anda dapat mengonversinya menjadi array tupel:

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

Untuk kebalikannya, gunakan

map = new Map(JSON.parse(jsonText));
Bergi
sumber
6
Ini tidak diubah menjadi objek JSON, melainkan menjadi Array of array. Bukan hal yang sama. Simak jawaban Evan Carroll di bawah ini untuk jawaban yang lebih lengkap.
Sab Thiru
3
@SatThiru Sebuah array tupel adalah representasi umum dari Maps, ini cocok dengan konstruktor dan iterator. Juga merupakan satu-satunya representasi peta yang memiliki kunci non-string, dan objek tidak akan berfungsi di sana.
Bergi
Bergi, harap dicatat bahwa OP mengatakan "Kunci saya dijamin string".
Sab Thiru
@Bergi Stringify tidak berfungsi jika itu keyadalah objek misalnya "{"[object Object]":{"b":2}}"- kunci objek menjadi salah satu fitur utama Maps
Drenai
60

Keduanya JSON.stringifydan JSON.parsemendukung argumen kedua. replacerdan revivermasing - masing. Dengan replacer dan reviver di bawahnya, Anda dapat menambahkan dukungan untuk objek Map asli, termasuk nilai yang sangat bertingkat

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

Penggunaan :

const originalValue = new Map([['a', 1]]);
const str = JSON.stringify(originalValue, replacer);
const newValue = JSON.parse(str, reviver);
console.log(originalValue, newValue);

Deep nesting dengan kombinasi Array, Objects, dan Maps

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);
Pawel
sumber
8
Ini adalah tanggapan terbaik sejauh ini
Manuel Vera Silvestre
1
Pasti jawaban terbaik disini.
JavaRunner
15

Meskipun belum ada metode yang disediakan oleh ecmascript, ini masih dapat dilakukan JSON.stingifyjika Anda memetakan Mapke JavaScript primitif. Ini contoh yang Mapakan kami gunakan.

const map = new Map();
map.set('foo', 'bar');
map.set('baz', 'quz');

Pergi ke Objek JavaScript

Anda dapat mengonversi ke literal Objek JavaScript dengan fungsi pembantu berikut.

const mapToObj = m => {
  return Array.from(m).reduce((obj, [key, value]) => {
    obj[key] = value;
    return obj;
  }, {});
};

JSON.stringify(mapToObj(map)); // '{"foo":"bar","baz":"quz"}'

Pergi ke JavaScript Array of Objects

Fungsi helper untuk yang satu ini akan lebih ringkas

const mapToAoO = m => {
  return Array.from(m).map( ([k,v]) => {return {[k]:v}} );
};

JSON.stringify(mapToAoO(map)); // '[{"foo":"bar"},{"baz":"quz"}]'

Pergi ke Array of Arrays

Ini bahkan lebih mudah, Anda bisa menggunakan

JSON.stringify( Array.from(map) ); // '[["foo","bar"],["baz","quz"]]'
Evan Carroll
sumber
13

Menggunakan peta sytax tersebar dapat diserialkan dalam satu baris:

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

dan deserialisasi dengan:

let map = new Map(JSON.parse(map));
metodribik
sumber
Ini akan berfungsi untuk Peta satu dimensi, tetapi tidak untuk peta n-dimensi.
mattsven
6

Merangkai sebuah Mapinstance (objek sebagai kunci OK) :

JSON.stringify([...map])

atau

JSON.stringify(Array.from(map))

atau

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

format output:

// [["key1","value1"],["key2","value2"]]
am0wa
sumber
4

Solusi yang Lebih Baik

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

Keluaran

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

Harapan yang kurang ngeri dari jawaban di atas.

Cody
sumber
Saya tidak yakin banyak yang akan puas dengan memperluas kelas peta inti hanya untuk
menserialisasinya
1
Mereka tidak harus seperti itu, tetapi ini cara yang lebih SOLID untuk melakukannya. Secara khusus, ini sejalan dengan prinsip LSP dan OCP dari SOLID. Artinya, Peta asli sedang diperpanjang, tidak dimodifikasi, dan seseorang masih dapat menggunakan Pergantian Liskov (LSP) dengan Peta asli. Memang, ini lebih merupakan OOP daripada yang disukai oleh banyak pemula atau Pemrograman Fungsional yang setia, tetapi setidaknya ini diliputi oleh dasar yang telah dicoba & benar dari prinsip-prinsip desain perangkat lunak dasar. Jika Anda ingin mengimplementasikan Interface Segregation Principle (ISP) SOLID, Anda dapat memiliki IJSONAbleantarmuka kecil (tentu saja menggunakan TypeScript).
Cody
3

Solusi di bawah ini berfungsi bahkan jika Anda memiliki Peta bersarang

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);
}
Imamudin Naseem
sumber
Tanpa menguji jawaban Anda, saya sudah menghargai bagaimana hal itu menarik perhatian pada masalah Peta bersarang. Meskipun Anda berhasil mengonversinya menjadi JSON, penguraian apa pun yang dilakukan di masa mendatang harus memiliki kesadaran eksplisit bahwa JSON pada awalnya adalah Mapdan (bahkan lebih buruk) bahwa setiap sub-peta (yang ada di dalamnya) juga aslinya adalah peta. Jika tidak, tidak ada cara untuk memastikan bahwa an array of pairstidak hanya dimaksudkan untuk menjadi persis seperti itu, alih-alih Peta. Hierarki objek dan larik tidak membawa beban ini saat diurai. Setiap serialisasi yang tepat Mapakan secara eksplisit menunjukkan bahwa itu adalah a Map.
Lonnie Best
Lebih lanjut tentang itu di sini .
Lonnie Terbaik