Meu aplicativo tem uma grande variedade de objetos, que eu stringi e os salvei no disco. Infelizmente, quando os objetos no array são manipulados e, às vezes, substituídos, as propriedades dos objetos são listadas em ordens diferentes (sua ordem de criação?). Quando eu faço JSON.stringify () no array e o salvo, um diff mostra as propriedades sendo listadas em diferentes ordens, o que é irritante ao tentar mesclar os dados com ferramentas de diff e mesclagem.
Idealmente, gostaria de classificar as propriedades dos objetos em ordem alfabética antes de executar o stringify ou como parte da operação de stringify. Existe código para manipular os objetos de array em muitos lugares, e alterá-los para sempre criar propriedades em uma ordem explícita seria difícil.
Sugestões serão muito bem-vindas!
Um exemplo condensado:
obj = {}; obj.name="X"; obj.os="linux";
JSON.stringify(obj);
obj = {}; obj.os="linux"; obj.name="X";
JSON.stringify(obj);
A saída dessas duas chamadas de stringify são diferentes e aparecem em uma comparação de meus dados, mas meu aplicativo não se preocupa com a ordem das propriedades. Os objetos são construídos de muitas maneiras e lugares.
fonte
Respostas:
A abordagem mais simples, moderna e atualmente compatível com o navegador é simplesmente esta:
No entanto, esse método remove todos os objetos aninhados que não são referenciados e não se aplicam a objetos dentro de matrizes. Você também desejará nivelar o objeto de classificação se quiser algo como esta saída:
Você pode fazer isso com isto:
Para fazer isso programaticamente com algo que você mesmo pode ajustar, você precisa colocar os nomes das propriedades do objeto em uma matriz, então classificar a matriz em ordem alfabética e iterar por meio dessa matriz (que estará na ordem certa) e selecionar cada valor do objeto em esse pedido. "hasOwnProperty" também está marcada para que você definitivamente tenha apenas as propriedades do próprio objeto. Aqui está um exemplo:
Novamente, isso deve garantir que você itere em ordem alfabética.
Por fim, levando-o adiante da maneira mais simples, esta biblioteca permitirá que você classifique recursivamente qualquer JSON que passar para ela: https://www.npmjs.com/package/json-stable-stringify
Resultado
fonte
obj[i]
no loopi
for porque é um número inteiro e os nomes das propriedades não são necessariamente (daí esta questão). Deve serobj[arr[i]]
.iterateObjectAlphabetically
em uma função reutilizável. Sem o retorno de chamada, o 'código de carga útil' teria que estar dentro da própria função. Eu acho que é uma solução muito elegante e que é usada em muitas bibliotecas e no próprio JS core. Por exemplo, Array.forEach .Acho que se você está no controle da geração JSON (e parece que está), então, para seus propósitos, esta pode ser uma boa solução: json-stable-stringify
No site do projeto:
Se o JSON produzido for determinístico, você poderá diferenciá-lo / mesclá-lo facilmente.
fonte
Você pode passar uma matriz classificada dos nomes das propriedades como o segundo argumento de
JSON.stringify()
:fonte
JSON.stringify()
é chamadoreplacer
e pode ser um array. Ele é usado para construir umPropertyList
que é então usadoSerializeJSONObject()
para anexar as propriedades nessa ordem. Isso está documentado desde ECMAScript 5.> JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}'
uma maneira (hacky) de construir a lista de todas as chaves relevantes é usar JSON.stringify para atravessar o objeto:function orderedStringify(obj) { const allKeys = []; JSON.stringify(obj, (k, v) => { allKeys.push(k); return v; }); return JSON.stringify(obj, allKeys.sort()); }
Exemplo:> orderedStringify({a: {c: 1, b: 2}}) '{"a":{"b":2,"c":1}}'
Não entendo por que a complexidade das melhores respostas atuais é necessária para obter todas as chaves recursivamente. A menos que seja necessário um desempenho perfeito, parece-me que podemos simplesmente ligar
JSON.stringify()
duas vezes, a primeira para obter todas as chaves e, na segunda vez, para realmente fazer o trabalho. Dessa forma, toda a complexidade da recursão é tratada porstringify
, e sabemos que ele conhece suas coisas e como lidar com cada tipo de objeto:fonte
Object.keys()
não é recursivo, portanto, se você tiver um objeto como{a: "A", b: {c: "C"} }
e chamar Object.keys nele, você receberá apenas as chavesa
eb
, mas nãoc
.JSON.stringify
sabe tudo sobre recursão em cada tipo de objeto tratado pelo processo de serialização JSON, seja um objeto ou um array (e já implementa a lógica para reconhecer ambos), então deve tratar de tudo corretamente para nós.Atualização em 24/07/2018:
Esta versão classifica objetos aninhados e também suporta array:
Caso de teste:
Resposta preterida:
Uma versão concisa no ES2016. Crédito para @codename, de https://stackoverflow.com/a/29622653/94148
fonte
function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
sortObjByKey()
verifica as referências circulares, então tome cuidado, ele pode travar em um loop infinito dependendo dos dados de entrada. Exemplo:var objA = {}; var objB = {objA: objA}; objA.objB = objB;
->sortObjByKey(objA);
->VM59:6 Uncaught RangeError: Maximum call stack size exceeded
https://gist.github.com/davidfurlong/463a83a33b70a3b6618e97ec9679e490
fonte
Esta é a mesma resposta de Satpal Singh
fonte
Você pode adicionar uma
toJSON
função personalizada ao seu objeto, que pode ser usada para personalizar a saída. Dentro da função, adicionar propriedades atuais a um novo objeto em uma ordem específica deve preservar essa ordem quando for stringificada.Veja aqui:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify
Não há nenhum método embutido para controlar a ordem porque os dados JSON devem ser acessados por chaves.
Aqui está um jsfiddle com um pequeno exemplo:
http://jsfiddle.net/Eq2Yw/
Experimente comentar a
toJSON
função - a ordem das propriedades é invertida. Esteja ciente de que isso pode ser específico do navegador, ou seja, o pedido não é oficialmente suportado na especificação. Ele funciona na versão atual do Firefox, mas se você deseja uma solução 100% robusta, pode ser necessário escrever sua própria função de restrição.Editar:
Veja também esta pergunta do SO sobre a saída não determinística do stringify, especialmente os detalhes de Daff sobre as diferenças do navegador:
Como verificar de forma determinística se um objeto JSON não foi modificado?
fonte
Uma resposta recursiva e simplificada:
fonte
reorder
deve ser renomeado parasortObject
e isso não lida com matrizestypeof arr === "object"
que isso transformaria matrizes em objetos. Por exemplo,var obj = {foo: ["bar", "baz"]}
seria transformado em{ "foo": { "0": "bar", "1": "baz"} }
if(obj.constructor.prototype !== Object.prototype)
Você pode classificar o objeto por nome de propriedade no EcmaScript 2015
fonte
sortObjectPropertiesByName()
ou mais simplessortPropertiesByName()
.Peguei a resposta de @Jason Parham e fiz algumas melhorias
Isso corrige o problema de arrays serem convertidos em objetos e também permite definir como classificar arrays.
Exemplo:
resultado:
fonte
A resposta aceita não funciona para mim para objetos aninhados por algum motivo. Isso me levou a codificar o meu próprio. Como é final de 2019 quando escrevo isto, existem mais algumas opções disponíveis no idioma.
Atualização: Eu acredito que a resposta de David Furlong é uma abordagem preferível à minha tentativa anterior, e eu a retirei. O meu conta com suporte para Object.entries (...), portanto, não há suporte para Internet Explorer.
-
MANTER ESTA VERSÃO ANTIGA PARA REFERÊNCIA HISTÓRICA
Descobri que uma matriz simples e plana de todas as chaves do objeto funcionará. Em quase todos os navegadores (não Edge ou Internet explorer, previsivelmente) e Node 12+, há uma solução bastante curta agora que Array.prototype.flatMap (...) está disponível. (O equivalente lodash também funcionaria.) Eu testei apenas no Safari, Chrome e Firefox, mas não vejo razão para que não funcione em qualquer outro lugar que suporte flatMap e JSON.stringify padrão (...) .
Com isso, você pode sequenciar sem dependências de terceiros e até mesmo passar seu próprio algoritmo de classificação que classifica os pares de entrada de valor-chave, para que você possa classificar por chave, carga útil ou uma combinação dos dois. Funciona para objetos aninhados, matrizes e qualquer combinação de tipos de dados simples e antigos.
Impressões:
Se você deve ter compatibilidade com mecanismos JavaScript mais antigos , pode usar essas versões um pouco mais detalhadas que emulam o comportamento flatMap. O cliente deve suportar pelo menos ES5, portanto, nenhum Internet Explorer 8 ou inferior.
Eles retornarão o mesmo resultado acima.
fonte
Funciona com lodash, objetos aninhados, qualquer valor de atributo de objeto:
Ele depende do comportamento do Chrome e do Node que a primeira chave atribuída a um objeto é emitida primeiro por
JSON.stringify
.fonte
Experimentar:
fonte
Eu fiz uma função para classificar o objeto, e com retorno de chamada .. que na verdade cria um novo objeto
e chamá-lo com objeto.
que imprime
{"value":"msio","os":"windows","name":"anu"}
e para classificar com valor.quais impressões
{"os":"windows","value":"msio","name":"anu"}
fonte
Se os objetos na lista não tiverem as mesmas propriedades, gere um objeto mestre combinado antes de stringify:
fonte
// exemplo conforme abaixo. em cada camada, para objetos como {}, achatados em classificação chave. para matrizes, números ou strings, achatados como / com JSON.stringify.
fonte
Estendendo a resposta da AJP para lidar com matrizes:
fonte
Surpreso que ninguém tenha mencionado a
isEqual
função de Lodash .Com o problema original - as chaves sendo ordenadas de forma inconsistente - é uma ótima solução - e é claro que irá parar se encontrar um conflito em vez de serializar cegamente o objeto inteiro.
Para evitar a importação de toda a biblioteca, faça o seguinte:
Exemplo de bônus: você também pode usar isso com RxJS com este operador personalizado
fonte
Afinal, ele precisa de um Array que armazene em cache todas as chaves no objeto aninhado (caso contrário, ele omitirá as chaves não armazenadas em cache). A resposta mais antiga está simplesmente errada, porque o segundo argumento não se preocupa com a notação de ponto. Então, a resposta (usando Set) torna-se.
fonte
Aqui está uma abordagem de clone ... clone o objeto antes de converter para json:
É muito novo, mas parece funcionar bem em arquivos package.json.
fonte
Existe um
Array.sort
método que pode ser útil para você. Por exemplo:fonte