javascript - parse - ordenar las propiedades del objeto y JSON.stringify
leer json externo con javascript (17)
Mi aplicación tiene una gran variedad de objetos, que he stringify y guardar en el disco. Desafortunadamente, cuando los objetos en la matriz son manipulados, y algunas veces reemplazados, las propiedades de los objetos se enumeran en diferentes órdenes (¿su orden de creación?). Cuando hago JSON.stringify () en la matriz y la guardo, un diff muestra las propiedades que se enumeran en diferentes órdenes, lo cual es molesto cuando se trata de fusionar los datos con herramientas diff y de fusión.
Idealmente, me gustaría ordenar las propiedades de los objetos en orden alfabético antes de realizar el stringify, o como parte de la operación de stringify. Existe un código para manipular los objetos de matriz en muchos lugares, y sería difícil modificarlos para crear siempre propiedades en un orden explícito.
¡Las sugerencias serían bienvenidas!
Un ejemplo condensado:
obj = {}; obj.name="X"; obj.os="linux";
JSON.stringify(obj);
obj = {}; obj.os="linux"; obj.name="X";
JSON.stringify(obj);
La salida de estas dos llamadas a stringify es diferente, y aparece en una diferencia de mis datos, pero a mi aplicación no le importa el orden de las propiedades ... los objetos se construyen de muchas formas y lugares.
Creo que si tienes el control de la generación JSON (y parece que lo eres), entonces para tus propósitos esta podría ser una buena solución: json-stable-stringify
Desde el sitio web del proyecto:
determinista JSON.stringify () con clasificación personalizada para obtener hashes determinísticos a partir de resultados codificados
Si el JSON producido es determinista, debería ser capaz de distinguirlo / fusionarlo fácilmente.
El enfoque más simple, moderno y actualmente compatible con el navegador es simplemente esto:
JSON.stringify(sortMyObj, Object.keys(sortMyObj).sort());
Sin embargo, este método elimina cualquier objeto anidado que no esté referenciado. También querrás aplanar el objeto de clasificación si quieres algo como esta salida:
{"a":{"h":4,"z":3},"b":2,"c":1}
Puedes hacer eso con esto:
var flattenObject = function(ob) {
var toReturn = {};
for (var i in ob) {
if (!ob.hasOwnProperty(i)) continue;
if ((typeof ob[i]) == ''object'') {
var flatObject = flattenObject(ob[i]);
for (var x in flatObject) {
if (!flatObject.hasOwnProperty(x)) continue;
toReturn[i + ''.'' + x] = flatObject[x];
}
} else {
toReturn[i] = ob[i];
}
}
return toReturn;
};
JSON.stringify(sortMyObj, Object.keys(flattenObject(sortMyObj)).sort());
Para hacerlo mediante programación con algo que pueda modificar, debe insertar los nombres de propiedad del objeto en una matriz, luego ordenar la matriz alfabéticamente e iterar a través de esa matriz (que estará en el orden correcto) y seleccionar cada valor del objeto en ese orden "hasOwnProperty" también está marcado, por lo que definitivamente solo tiene las propiedades propias del objeto. Aquí hay un ejemplo:
var obj = {"a":1,"b":2,"c":3};
function iterateObjectAlphabetically(obj, callback) {
var arr = [],
i;
for (i in obj) {
if (obj.hasOwnProperty(i)) {
arr.push(i);
}
}
arr.sort();
for (i = 0; i < arr.length; i++) {
var key = obj[arr[i]];
//console.log( obj[arr[i]] ); //here is the sorted value
//do what you want with the object property
if (callback) {
// callback returns arguments for value, key and original object
callback(obj[arr[i]], arr[i], obj);
}
}
}
iterateObjectAlphabetically(obj, function(val, key, obj) {
//do something here
});
De nuevo, esto debería garantizar que itere en orden alfabético.
Finalmente, llevándolo más allá de la manera más simple, esta biblioteca le permitirá recursivamente ordenar cualquier JSON que pase a él: https://www.npmjs.com/package/json-stable-stringify
var stringify = require(''json-stable-stringify'');
var obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };
console.log(stringify(obj));
Salida
{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}
Esto es lo mismo que la respuesta de Satpal Singh
function stringifyJSON(obj){
keys = [];
if(obj){
for(var key in obj){
keys.push(key);
}
}
keys.sort();
var tObj = {};
var key;
for(var index in keys){
key = keys[index];
tObj[ key ] = obj[ key ];
}
return JSON.stringify(tObj);
}
obj1 = {}; obj1.os="linux"; obj1.name="X";
stringifyJSON(obj1); //returns "{"name":"X","os":"linux"}"
obj2 = {}; obj2.name="X"; obj2.os="linux";
stringifyJSON(obj2); //returns "{"name":"X","os":"linux"}"
Extendiendo la respuesta de AJP, para manejar arreglos:
function sort(myObj) {
var sortedObj = {};
Object.keys(myObj).sort().forEach(key => {
sortedObj[key] = _.isPlainObject(myObj[key]) ? sort(myObj[key]) : _.isArray(myObj[key])? myObj[key].map(sort) : myObj[key]
})
return sortedObj;
}
Funciona con lodash, objetos anidados, cualquier valor de atributo de objeto:
function sort(myObj) {
var sortedObj = {};
Object.keys(myObj).sort().forEach(key => {
sortedObj[key] = _.isPlainObject(myObj[key]) ? sort(myObj[key]) : myObj[key]
})
return sortedObj;
}
JSON.stringify(sort(yourObj), null, 2)
Se basa en el comportamiento de Chrome y Node de que la primera clave asignada a un objeto es JSON.stringify
primero por JSON.stringify
.
Hay Array.sort
método Array.sort
que puede ser útil para usted. Por ejemplo:
yourBigArray.sort(function(a,b){
//custom sorting mechanism
});
Hice una función para ordenar el objeto, y con la devolución de llamada ... que realmente crea un nuevo objeto
function sortObj( obj , callback ) {
var r = [] ;
for ( var i in obj ){
if ( obj.hasOwnProperty( i ) ) {
r.push( { key: i , value : obj[i] } );
}
}
return r.sort( callback ).reduce( function( obj , n ){
obj[ n.key ] = n.value ;
return obj;
},{});
}
y llámalo con objeto.
var obj = {
name : "anu",
os : "windows",
value : ''msio'',
};
var result = sortObj( obj , function( a, b ){
return a.key < b.key ;
});
JSON.stringify( result )
que imprime {"value":"msio","os":"windows","name":"anu"}
, y para ordenar con valor.
var result = sortObj( obj , function( a, b ){
return a.value < b.value ;
});
JSON.stringify( result )
que imprime {"os":"windows","value":"msio","name":"anu"}
Puede agregar una función toJSON
personalizada a su objeto que puede usar para personalizar la salida. Dentro de la función, agregar propiedades actuales a un nuevo objeto en un orden específico debe preservar ese orden cuando se realiza una cadena.
Mira aquí:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify
No existe un método integrado para controlar el orden porque los datos JSON deben accederse mediante claves.
Aquí hay un jsfiddle con un pequeño ejemplo:
Intente comentar la función toJSON
: el orden de las propiedades se invierte. Tenga en cuenta que esto puede ser específico del navegador, es decir, el pedido no está oficialmente admitido en la especificación. Funciona en la versión actual de Firefox, pero si desea una solución 100% sólida, puede que tenga que escribir su propia función de secuenciador.
Editar:
También vea esta pregunta SO sobre la salida no determinista de stringify, especialmente los detalles de Daff sobre las diferencias del navegador:
¿Cómo verificar determinísticamente que un objeto JSON no ha sido modificado?
Puede ordenar el objeto por nombre de propiedad en EcmaScript 2015
function sortObjectByPropertyName(obj) {
return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {});
}
Puede pasar una matriz ordenada de los nombres de propiedades como el segundo argumento de JSON.stringify()
:
JSON.stringify(obj, Object.keys(obj).sort())
Si los objetos en la lista no tienen las mismas propiedades, genere un objeto maestro combinado antes de stringify:
let arr=[ <object1>, <object2>, ... ]
let o = {}
for ( let i = 0; i < arr.length; i++ ) {
Object.assign( o, arr[i] );
}
JSON.stringify( arr, Object.keys( o ).sort() );
Tomé la respuesta de @Jason Parham e hice algunas mejoras
function sortObject(obj, arraySorter) {
if(typeof obj !== ''object'')
return obj
if (Array.isArray(obj)) {
if (arraySorter) {
obj.sort(arraySorter);
}
for (var i = 0; i < obj.length; i++) {
obj[i] = sortObject(obj[i], arraySorter);
}
return obj;
}
var temp = {};
var keys = [];
for(var key in obj)
keys.push(key);
keys.sort();
for(var index in keys)
temp[keys[index]] = sortObject(obj[keys[index]], arraySorter);
return temp;
}
Esto soluciona el problema de las matrices que se convierten en objetos, y también le permite definir cómo ordenar las matrices.
Ejemplo:
var data = { content: [{id: 3}, {id: 1}, {id: 2}] };
sortObject(data, (i1, i2) => i1.id - i2.id)
salida:
{content:[{id:1},{id:2},{id:3}]}
Tratar:
function obj(){
this.name = '''';
this.os = '''';
}
a = new obj();
a.name = ''X'',
a.os = ''linux'';
JSON.stringify(a);
b = new obj();
b.os = ''linux'';
b.name = ''X'',
JSON.stringify(b);
Una respuesta recursiva y simplificada:
function sortObject(obj) {
if(typeof obj !== ''object'')
return obj
var temp = {};
var keys = [];
for(var key in obj)
keys.push(key);
keys.sort();
for(var index in keys)
temp[keys[index]] = sortObject(obj[keys[index]]);
return temp;
}
var str = JSON.stringify(sortObject(obj), undefined, 4);
https://gist.github.com/davidfurlong/463a83a33b70a3b6618e97ec9679e490
const replacer = (key, value) =>
value instanceof Object && !(value instanceof Array) ?
Object.keys(value)
.sort()
.reduce((sorted, key) => {
sorted[key] = value[key];
return sorted
}, {}) :
value;
Actualización 2018-7-24:
Esta versión ordena los objetos anidados y admite la matriz también:
function sortObjByKey(value) {
return (typeof value === ''object'') ?
(Array.isArray(value) ?
value.map(sortObjByKey) :
Object.keys(value).sort().reduce(
(o, key) => {
const v = value[key];
o[key] = sortObjByKey(v);
return o;
}, {})
) :
value;
}
function orderedJsonStringify(obj) {
return JSON.stringify(sortObjPropertiesByKey(obj));
}
Caso de prueba:
describe(''orderedJsonStringify'', () => {
it(''make properties in order'', () => {
const obj = {
name: ''foo'',
arr: [
{ x: 1, y: 2 },
{ y: 4, x: 3 },
],
value: { y: 2, x: 1, },
};
expect(orderedJsonStringify(obj))
.to.equal(''{"arr":[{"x":1,"y":2},{"x":3,"y":4}],"name":"foo","value":{"x":1,"y":2}}'');
});
it(''support array'', () => {
const obj = [
{ x: 1, y: 2 },
{ y: 4, x: 3 },
];
expect(orderedJsonStringify(obj))
.to.equal(''[{"x":1,"y":2},{"x":3,"y":4}]'');
});
});
Respuesta obsoleta:
Una versión concisa en ES2016. Crédito a @codename, de https://.com/a/29622653/94148
function orderedJsonStringify(o) {
return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {}));
}
function FlatternInSort( obj ) {
if( typeof obj === ''object'' )
{
if( obj.constructor === Object )
{ //here use underscore.js
let PaireStr = _( obj ).chain().pairs().sortBy( p => p[0] ).map( p => p.map( FlatternInSort ).join( '':'' )).value().join( '','' );
return ''{'' + PaireStr + ''}'';
}
return ''['' + obj.map( FlatternInSort ).join( '','' ) + '']'';
}
return JSON.stringify( obj );
}
// ejemplo como a continuación. en cada capa, para objetos como {}, aplanados en orden de clave. para arreglos, números o cadenas, aplanados como / con JSON.stringify.
FlatternInSort ({c: 9, b: {y: 4, z: 2, e: 9}, F: 4, a: [{j: 8, h: 3}, {a: 3, b: 7}] })
"{" F ": 4," a ": [{" h ": 3," j ": 8}, {" a ": 3," b ": 7}]," b ": {" e " : 9, "y": 4, "z": 2}, "c": 9} "