it-swarm.com.de

sortiereigenschaften und JSON.stringify

Meine Anwendung verfügt über ein großes Array von Objekten, die ich stringify und auf der Festplatte speichere. Wenn die Objekte im Array manipuliert und manchmal ersetzt werden, werden die Eigenschaften der Objekte in unterschiedlicher Reihenfolge aufgeführt (Reihenfolge der Erstellung?). Wenn ich JSON.stringify () für das Array mache und es speichere, zeigt ein Vergleich die Eigenschaften, die in unterschiedlicher Reihenfolge aufgeführt werden. Dies ist ärgerlich, wenn Sie versuchen, die Daten mit den Vergleichs- und Zusammenführungswerkzeugen weiter zusammenzuführen.

Im Idealfall möchte ich die Eigenschaften der Objekte in alphabetischer Reihenfolge vor dem Ausführen des stringify oder als Teil der stringify-Operation sortieren. Es gibt Code zum Manipulieren der Array-Objekte an vielen Stellen, und es wäre schwierig, diese zu ändern, um Eigenschaften immer in einer expliziten Reihenfolge zu erstellen. 

Anregungen wären sehr willkommen!

Ein konkretes Beispiel:

obj = {}; obj.name="X"; obj.os="linux";
JSON.stringify(obj);
obj = {}; obj.os="linux"; obj.name="X";
JSON.stringify(obj);

Die Ausgabe dieser beiden stringify-Aufrufe ist unterschiedlich und zeigt sich in einem Unterschied meiner Daten, aber meine Anwendung kümmert sich nicht um die Reihenfolge der Eigenschaften. Die Objekte sind in vielerlei Hinsicht an verschiedenen Orten konstruiert.

60
Innovine

Der einfachere, modernste und derzeit vom Browser unterstützte Ansatz lautet einfach:

JSON.stringify(sortMyObj, Object.keys(sortMyObj).sort());

Diese Methode entfernt jedoch verschachtelte Objekte, auf die nicht verwiesen wird, und gilt nicht für Objekte in Arrays. Sie möchten auch das Sortierobjekt reduzieren, wenn Sie eine Ausgabe wie diese wünschen:

{"a":{"h":4,"z":3},"b":2,"c":1}

Das kannst du damit machen:

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

Um es programmgesteuert mit etwas zu tun, das Sie selbst anpassen können, müssen Sie die Objekt-Eigenschaftsnamen in ein Array schieben, das Array alphabetisch sortieren, dieses Array durchlaufen (in der richtigen Reihenfolge sein) und jeden Wert aus dem Objekt in auswählen diese Reihenfolge "hasOwnProperty" wird ebenfalls geprüft, so dass Sie definitiv nur die eigenen Eigenschaften des Objekts haben. Hier ist ein Beispiel:

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

Dies sollte wiederum sicherstellen, dass Sie in alphabetischer Reihenfolge durchlaufen. 

Um es für den einfachsten Weg weiter auszudrücken, erlaubt Ihnen diese Bibliothek rekursiv, alle darin enthaltenen JSON-Dateien zu sortieren: 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));

Ausgabe

{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}
46
marksyzm

Ich denke, wenn Sie die Kontrolle über die JSON-Generation haben (und es klingt wie Sie), dann könnte dies für Ihre Zwecke eine gute Lösung sein: json-stable-stringify

Von der Projektwebsite:

deterministisches JSON.stringify () mit benutzerdefinierter Sortierung zum Abrufen von deterministische Hashes aus stringifizierten Ergebnissen

Wenn das erzeugte JSON deterministisch ist, sollten Sie es leicht unterscheiden/zusammenführen können.

29
Stijn de Witt

Sie können ein sortiertes Array der Eigenschaftsnamen als zweites Argument von JSON.stringify() übergeben:

JSON.stringify(obj, Object.keys(obj).sort())
16

Update 2018-7-24: 

Diese Version sortiert verschachtelte Objekte und unterstützt auch Array:

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(sortObjByKey(obj));
}

Testfall:

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

  });

Veraltete Antwort:

Eine prägnante Version in ES2016 . Kredit an @codename, von https://stackoverflow.com/a/29622653/94148

function orderedJsonStringify(o) {
  return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {}));
}
13
aleung

Dies ist die gleiche Antwort von 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"}"
4
Giridhar C R

Eine rekursive und vereinfachte Antwort:

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);
3
Jason Parham

Ich nahm die Antwort von @ Jason Parham und machte einige Verbesserungen

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

Dies behebt das Problem, dass Arrays in Objekte konvertiert werden. Außerdem können Sie definieren, wie Arrays sortiert werden.

Beispiel:

var data = { content: [{id: 3}, {id: 1}, {id: 2}] };
sortObject(data, (i1, i2) => i1.id - i2.id)

ausgabe:

{content:[{id:1},{id:2},{id:3}]}
3
Peter

Sie können Ihrem Objekt eine benutzerdefinierte toJSON-Funktion hinzufügen, mit der Sie die Ausgabe anpassen können. Durch das Hinzufügen von aktuellen Eigenschaften zu einem neuen Objekt in einer bestimmten Reihenfolge sollte diese Reihenfolge in der Funktion beibehalten werden, wenn sie stringifiziert ist.

Siehe hier:

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify

Es gibt keine integrierte Methode zur Steuerung der Reihenfolge, da auf JSON-Daten über Schlüssel zugegriffen werden soll.

Hier ist ein jsfiddle mit einem kleinen Beispiel:

http://jsfiddle.net/Eq2Yw/

Versuchen Sie, die Funktion toJSON zu kommentieren - die Reihenfolge der Eigenschaften ist umgekehrt. Bitte beachten Sie, dass dies browserspezifisch sein kann, d. H., Die Bestellung wird in der Spezifikation nicht offiziell unterstützt. Es funktioniert in der aktuellen Version von Firefox, aber wenn Sie eine 100% robuste Lösung suchen, müssen Sie möglicherweise eine eigene Stringifier-Funktion schreiben.

Bearbeiten:

Siehe auch diese SO - Frage bezüglich der nicht deterministischen Ausgabe von stringify, insbesondere Daffs Details zu Browserdifferenzen:

Wie deterministisch verifizieren, dass ein JSON-Objekt nicht geändert wurde?

3
Dave R.

Ich verstehe nicht, warum die Komplexität der aktuell besten Antworten erforderlich ist, um alle Schlüssel rekursiv zu erhalten (kann sie jedoch aufgrund von Reputationsbeschränkungen nicht kommentieren ...). Wenn keine perfekte Leistung erforderlich ist, scheint es mir, dass wir einfach JSON.stringify() zweimal aufrufen können, das erste Mal, um alle Schlüssel zu erhalten, und das zweite Mal, um die Arbeit wirklich zu erledigen. Auf diese Weise wird die gesamte Rekursionskomplexität von stringify verarbeitet, und wir wissen, dass sie über alles Bescheid weiß und wie mit jedem Objekttyp umzugehen ist:

function JSONstringifyOrder( obj, space )
{
    var allKeys = [];
    JSON.stringify( obj, function( key, value ){ allKeys.Push( key ); return value; } )
    allKeys.sort();
    return JSON.stringify( obj, allKeys, space );
}
2
Jor

Funktioniert mit lodash, verschachtelten Objekten und beliebigen Attributwerten:

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)

Es beruht auf dem Verhalten von Chrome und Node, dass der erste einem Objekt zugewiesene Schlüssel zuerst von JSON.stringify ausgegeben wird.

1
AJP

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;
1
David Furlong

Sie können das Objekt in EcmaScript 2015 nach dem Namen der Eigenschaft sortieren

function sortObjectByPropertyName(obj) {
    return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {});
}
1
mike

Überrascht hat niemand die Funktion isEqual von lodash erwähnt.

Führt einen umfassenden Vergleich zwischen zwei Werten durch, um festzustellen, ob sie gleichwertig sind.

Hinweis: Diese Methode unterstützt den Vergleich von Arrays, Array-Puffern, Booleschen Werten, Datumsobjekten, Fehlerobjekten, Zuordnungen, Zahlen, Objektobjekten, Regexen, Mengen, Zeichenfolgen, Symbolen und typisierten Arrays. Objektobjekte werden anhand ihrer eigenen, nicht geerbten, aufzählbaren Eigenschaften verglichen. Funktionen und DOM-Knoten werden durch strikte Gleichheit verglichen, d. H.

https://lodash.com/docs/4.17.11#isEqual

Mit dem ursprünglichen Problem, dass die Schlüssel uneinheitlich bestellt wurden, ist dies eine großartige Lösung. Natürlich hört das Problem auf, wenn ein Konflikt auftritt, anstatt das gesamte Objekt blind zu serialisieren.

So vermeiden Sie den Import der gesamten Bibliothek:

import { isEqual } from "lodash-es";

Bonus-Beispiel: Sie können dies auch mit RxJS mit diesem benutzerdefinierten Operator verwenden

export const distinctUntilEqualChanged = <T>(): MonoTypeOperatorFunction<T> => 
                                                pipe(distinctUntilChanged(isEqual));
0
Simon_Weaver

Versuchen:

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);
0
3y3

Wenn Objekte in der Liste nicht dieselben Eigenschaften haben, generieren Sie ein kombiniertes Master-Objekt, bevor Sie stringify ausführen:

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

0

Ich habe eine Funktion zum Sortieren von Objekten und mit Callback .. erstellt, die tatsächlich ein neues Objekt erstellen

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

und nenne es mit object.

var obj = {
    name : "anu",
    os : "windows",
    value : 'msio',
};

var result = sortObj( obj , function( a, b ){
    return a.key < b.key  ;    
});

JSON.stringify( result )

welche {"value":"msio","os":"windows","name":"anu"} druckt, und zum Sortieren nach Wert.

var result = sortObj( obj , function( a, b ){
    return a.value < b.value  ;    
});

JSON.stringify( result )

welche {"os":"windows","value":"msio","name":"anu"} druckt

0
rab

Erweiterung der Antwort von AJP auf Arrays:

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

// Beispiel wie unten. In jeder Ebene für Objekte wie {}, abgeflacht in Schlüsselsortierung. für Arrays, Zahlen oder Strings, die wie/mit JSON.stringify abgeflacht sind.

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

0
saintthor