Bir ES6 Haritasını JSON.stringify nasıl yaparsınız?


113

JS nesneleri yerine ES6 Haritasını kullanmaya başlamak istiyorum ama geri çekiliyorum çünkü JSON.stringify () a Map'i nasıl yapacağımı çözemiyorum. Anahtarlarımın dizi olması garantilidir ve değerlerim her zaman listelenecektir. Serileştirmek için gerçekten bir sarmalayıcı yöntemi yazmam gerekiyor mu?



Bunu çalıştırmayı başardım. Sonuçlar embed.plnkr.co/oNlQQBDyJUiIQlgWUPVP adresindeki Plunkr'da . Çözüm, bir Map nesnesinin geçirilip geçirilmediğini kontrol eden ve Map nesnesini bir Javascript nesnesine (JSON.stringify) dönüştüren bir JSON.stringify (obj, replaceerFunction) kullanır ve daha sonra bir dizeye dönüşür.
PatS

1
Anahtarlarınızın dizeler (veya sayılar) olması garantiliyse ve değer dizileriniz şöyle bir şey yapabilirsiniz [...someMap.entries()].join(';'); daha karmaşık bir şey için benzer bir şeyi deneyebilirsiniz[...someMap.entries()].reduce((acc, cur) => acc + `${cur[0]}:${/* do something to stringify cur[1] */ }`, '')
user56reinstatemonica8

@Oriol Ya anahtar adının varsayılan özelliklerle aynı olması mümkünse? obj[key]size beklenmedik bir şey getirebilir. Davayı düşünün if (!obj[key]) obj[key] = newList; else obj[key].mergeWith(newList);.
Franklin Yu

Yanıtlar:


81

İkisi de JSON.stringifyve JSON.parseikinci bir argümanı destekler. replacerve reviversırasıyla. Değiştirici ve değiştirici ile, derinlemesine iç içe geçmiş değerler dahil olmak üzere yerel Harita nesnesi için destek eklemek mümkündür

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

Kullanım :

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

Diziler, Nesneler ve Haritalar kombinasyonu ile derinlemesine yerleştirme

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

9
Şimdiye kadarki en iyi yanıt bu
Manuel Vera Silvestre

1
Kesinlikle en iyi cevap burada.
JavaRunner

Bunu doğru olarak işaretledim. Kablo boyunca verileri standart olmayan bir yöntemle "kirletmeniz" gerektiği gerçeğinden hoşlanmasam da dataType, daha temiz bir yol düşünemiyorum. Teşekkürler.
rynop

@rynop evet, saf yerel işlevselliği kullanmaktan çok kendi veri depolama biçimine sahip küçük bir program gibi
Pawel

77

MapHerhangi bir özelliğe sahip olmadığı için örneği doğrudan dizileme yapamazsınız , ancak onu bir demet dizisine dönüştürebilirsiniz:

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

Tersi için kullanın

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

6
Bu bir JSON nesnesine değil, bunun yerine bir dizi dizisine dönüşür. Aynı şey değil. Daha eksiksiz bir cevap için aşağıdaki Evan Carroll'un cevabına bakın.
Cts

3
@SatThiru Bir tuple dizisi s'nin geleneksel temsilidir Map, yapıcı ve yineleyici ile uyumludur . Ayrıca, dize olmayan anahtarlara sahip haritaların tek mantıklı temsilidir ve nesne orada çalışmaz.
Bergi

Bergi, lütfen OP'nin "Anahtarlarımın dizi olması garantilidir" dediğini unutmayın.
Cts


@Bergi Stringify key, örneğin bir nesne ise çalışmaz "{"[object Object]":{"b":2}}"- nesne anahtarları Haritalar'ın ana özelliklerinden biridir
Drenai

66

Yapamazsın.

Bir haritanın anahtarları, nesneler dahil herhangi bir şey olabilir. Ancak JSON sözdizimi yalnızca dizelere anahtar olarak izin verir. Yani genel bir durumda imkansızdır.

Anahtarlarımın dize olması garantilidir ve değerlerim her zaman liste olacaktır

Bu durumda düz bir nesne kullanabilirsiniz. Şu avantajlara sahip olacak:

  • JSON'a bağlanabilecektir.
  • Daha eski tarayıcılarda çalışacaktır.
  • Daha hızlı olabilir.

33
merak edilen en son krom için, herhangi bir harita '{}' olarak serileştirilir
Capaj

Burada "yapamazsın" derken tam olarak ne demek istediğimi açıkladım .
Oriol

8
"Daha hızlı olabilir" - Bununla ilgili herhangi bir kaynağınız var mı? Basit bir karma haritanın tamamen şişirilmiş bir nesneden daha hızlı olması gerektiğini düşünüyorum, ancak kanıtım yok. :)
Lilleman

1
@Xplouder Bu test pahalı kullanıyor hasOwnProperty. Bu olmadan, Firefox nesneleri haritalardan çok daha hızlı yineler. Yine de, haritalar Chrome'da hala daha hızlı. jsperf.com/es6-map-vs-object-properties/95
Oriol

1
Doğru, Firefox 45v'nin nesneleri Chrome + 49v'den daha hızlı yinelediği görülüyor. Ancak Haritalar yine de Chrome'daki nesnelere karşı kazanır.
Xplouder

17

Henüz ECMAScript'e tarafından sağlanan hiçbir yöntem olsa da, bu yine de kullanılarak yapılabilir JSON.stingifysen eşlerseniz Mapbir JavaScript ilkel için. İşte Mapkullanacağımız örnek .

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

Bir JavaScript Nesnesine Gitmek

Aşağıdaki yardımcı işlevle değişmez JavaScript Nesnesine dönüştürebilirsiniz.

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 Nesne Dizisine Gitme

Bunun için yardımcı işlev daha da kompakt olacaktır.

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

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

Dizi Dizisine Gitme

Bu daha da kolay, sadece kullanabilirsiniz

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

14

Kullanılması yayılma sytax Harita tek satırda serileştirilebilir:

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

ve şununla serisini kaldır:

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

Bu, tek boyutlu bir Harita için çalışacak, ancak n boyutlu bir harita için çalışmayacaktır.
mattsven

7

Bir Mapörneği dizgi (anahtar olarak nesneler uygundur) :

JSON.stringify([...map])

veya

JSON.stringify(Array.from(map))

veya

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

çıkış formatı:

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

4

Aşağıdaki çözüm, Haritalar'ı iç içe yerleştirseniz bile çalışır

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

Cevabınızı test etmeden, bunun iç içe geçmiş Haritalar sorununa nasıl dikkat çektiğini şimdiden takdir ediyorum. Bunu başarılı bir şekilde JSON'a dönüştürseniz bile, gelecekte yapılacak herhangi bir ayrıştırma, JSON'un başlangıçta bir Mapve (daha da kötüsü) her bir alt haritanın (içerdiği) aynı zamanda bir harita olduğu konusunda açık bir farkındalığa sahip olmalıdır . Aksi takdirde, array of pairsbir Harita yerine tam olarak öyle tasarlanmadığından emin olmanın bir yolu yoktur . Nesnelerin ve dizilerin hiyerarşileri, ayrıştırıldığında bu yükü taşımaz. Herhangi bir uygun serileştirme, Mapbunun bir Map.
Lonnie Best

Bununla ilgili daha fazla bilgiyi burada bulabilirsiniz .
Lonnie Best

4

Daha İyi Bir Çözüm

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

Çıktı

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

Umarım bu, yukarıdaki cevaplardan daha az zordur.


Birçoğunun çekirdek harita sınıfını sadece bir json'a serileştirmek için genişletmekten memnun olacağından emin değilim ...
vasia

1
Olmak zorunda değiller ama bunu yapmanın daha SAĞLAM bir yolu. Bu, özellikle SOLID'in LSP ve OCP ilkeleriyle uyumludur. Yani, yerel Harita genişletiliyor, değiştirilmiyor ve yine de Liskov İkamesi (LSP) yerel bir Harita ile kullanılabilir. Kabul edilirse, birçok aceminin veya sadık İşlevsel Programlama insanının tercih edeceğinden daha fazla OOP'dir, ancak en azından temel yazılım tasarım ilkelerinin denenmiş ve gerçek bir temeline bağlıdır. IJSONAbleSOLID'in Arayüz Ayırma Prensibini (ISP) uygulamak istiyorsanız, küçük bir arayüze sahip olabilirsiniz (elbette TypeScript kullanarak).
Cody

3

Örneğinizin, anahtarların basit türler olacağı basit bir kullanım durumu olduğu göz önüne alındığında, bunun JSON bir Haritayı dizgiselleştirmenin en kolay yolu olduğunu düşünüyorum.

JSON.stringify(Object.fromEntries(map));

Bir Haritanın temelindeki veri yapısı hakkında düşünme şeklim, anahtar-değer çiftlerinden oluşan bir dizidir (dizilerin kendileri olarak). Yani, bunun gibi bir şey:

const myMap = new Map([
     ["key1", "value1"],
     ["key2", "value2"],
     ["key3", "value3"]
]);

Bu temel veri yapısı, Object.entries'de bulduğumuz şey olduğu için, bir Array'de Object.fromEntries()olduğu gibi bir Map üzerinde yerel JavaScript yöntemini kullanabiliriz :

Object.fromEntries(myMap);

/*
{
     key1: "value1",
     key2: "value2",
     key3: "value3"
}
*/

Ve bunun sonucunda geriye kalan tek şey JSON.stringify () kullanmaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.