JSON stringify un ensemble

102

Comment un JSON.stringify () serait-il un ensemble ?

Choses qui ne fonctionnaient pas dans Chromium 43:

var s = new Set(['foo', 'bar']);

JSON.stringify(s); // -> "{}"
JSON.stringify(s.values()); // -> "{}"
JSON.stringify(s.keys()); // -> "{}"

Je m'attendrais à obtenir quelque chose de similaire à celui d'un tableau sérialisé.

JSON.stringify(["foo", "bar"]); // -> "["foo","bar"]"
MitMaro
la source

Réponses:

108

JSON.stringify ne fonctionne pas directement avec les ensembles car les données stockées dans l'ensemble ne sont pas stockées en tant que propriétés.

Mais vous pouvez convertir l'ensemble en tableau. Ensuite, vous pourrez le stringifier correctement.

N'importe lequel des éléments suivants fera l'affaire:

JSON.stringify([...s]);
JSON.stringify([...s.keys()]);
JSON.stringify([...s.values()]);
JSON.stringify(Array.from(s));
JSON.stringify(Array.from(s.keys()));
JSON.stringify(Array.from(s.values()));
Oriol
la source
2
J'allais proposer Array.from(). Mais cela semble mieux idiomiquement.
TaoPR
3
Juste pour ajouter quelques références, MDN a quelques exemples de cette technique et d'autres techniques connexes
Amit
5
Une compréhension de liste pourrait également fonctionner:JSON.stringify([_ for (_ of s)])
Whymarrh
1
Toutes les bonnes façons de le faire, malheureusement, elles ne fonctionnent pas dans Chromium 43 hors de la boîte car spread et Array.from ne sont pas encore implémentés. On dirait Babel à la rescousse.
MitMaro du
2
Une proposition actuelle pour améliorer la valeur par défaut de Set.prototype.toJSON: github.com/DavidBruant/Map-Set.prototype.toJSON
Oncle Tom
34

Utilisez ce JSON.stringifyremplaçant:

(car il toJSONs'agit d'un artefact hérité et une meilleure approche consiste à utiliser une personnalisationreplacer , voir https://github.com/DavidBruant/Map-Set.prototype.toJSON/issues/16 )

function Set_toJSON(key, value) {
  if (typeof value === 'object' && value instanceof Set) {
    return [...value];
  }
  return value;
}

Ensuite:

const fooBar = { foo: new Set([1, 2, 3]), bar: new Set([4, 5, 6]) };
JSON.stringify(fooBar, Set_toJSON)

Résultat:

"{"foo":[1,2,3],"bar":[4,5,6]}"
tanguy_k
la source
5
il y a une version raccourcie dans ES6JSON.stringify(fooBar, (key, value) => value instanceof Set ? [...value] : value)
OzzyCzech
Le reviveur correspondant est laissé comme exercice pour le lecteur? ;-)
Robert Siemer
7

Bien que tout ce qui précède fonctionne, je suggère que vous définissiez une sous-classe et ajoutiez un toJSON méthode pour vous assurer qu'elle est correctement stringifiée. Surtout si vous allez être souvent contraignant. J'utilise des sets dans mes magasins Redux et je devais m'assurer que ce n'était jamais un problème.

Ceci est une implémentation de base. Le nommage est juste pour illustrer le point de choisir votre propre style.

class JSONSet extends Set {
    toJSON () {
        return [...this]
    }
}

const set = new JSONSet([1, 2, 3])
console.log(JSON.stringify(set))
Stephen Bolton
la source
4
Je ne recommanderais pas cette approche car elle toJSONest considérée comme une fonctionnalité héritée. Une proposition d'ajout toJSONaux deux setet a mapété rejetée: github.com/DavidBruant/Map-Set.prototype.toJSON/issues/16
MitMaro
Le remplacement constructorn'est pas nécessaire.
Justin Johnson