JSON stringify um conjunto

99

Como seria um JSON.stringify () um Set ?

Coisas que não funcionaram no Chromium 43:

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

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

Eu esperaria obter algo semelhante ao de uma matriz serializada.

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

Respostas:

105

JSON.stringify não funciona diretamente com conjuntos porque os dados armazenados no conjunto não são armazenados como propriedades.

Mas você pode converter o conjunto em uma matriz. Então você será capaz de restringi-lo corretamente.

Qualquer uma das opções a seguir resolverá o problema:

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
fonte
2
Eu ia propor Array.from(). Mas isso parece idiomicamente melhor.
TaoPR
3
Apenas para adicionar algumas referências, o MDN tem alguns exemplos desta e de outras técnicas relacionadas
Amit
5
Uma compreensão de lista também pode funcionar:JSON.stringify([_ for (_ of s)])
Whymarrh
1
Todas ótimas maneiras de fazer isso, infelizmente, elas não funcionam no Chromium 43 fora da caixa, pois o spread e o Array.from ainda não foram implementados. Parece que Babel está ajudando.
MitMaro
2
Uma proposta atual para melhorar o padrão de Set.prototype.toJSON: github.com/DavidBruant/Map-Set.prototype.toJSON
Oncle Tom
34

Use este JSON.stringifysubstituto:

(porque toJSONé um artefato legado e uma abordagem melhor é usar um personalizadoreplacer , consulte 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;
}

Então:

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

Resultado:

"{"foo":[1,2,3],"bar":[4,5,6]}"
tanguy_k
fonte
5
há uma versão abreviada em ES6JSON.stringify(fooBar, (key, value) => value instanceof Set ? [...value] : value)
OzzyCzech
O reviver correspondente é deixado como um exercício para o leitor? ;-)
Robert Siemer
7

Embora todo o trabalho acima funcione, sugiro que você defina uma subclasse e adicione um toJSON método para ter certeza de que o stringify está correto. Especialmente se você for restringir com frequência. Eu uso conjuntos em minhas lojas Redux e precisava ter certeza de que isso nunca seria um problema.

Esta é uma implementação básica. Nomear é apenas para ilustrar o ponto, escolha seu próprio estilo.

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

const set = new JSONSet([1, 2, 3])
console.log(JSON.stringify(set))
Stephen Bolton
fonte
4
Eu não recomendaria essa abordagem, pois toJSONé considerada um recurso legado. Uma proposta para adicionar toJSONa ambos sete mapfoi rejeitada: github.com/DavidBruant/Map-Set.prototype.toJSON/issues/16
MitMaro
Substituir constructornão é necessário.
Justin Johnson