【问题标题】:sort object properties and JSON.stringify排序对象属性和 JSON.stringify
【发布时间】:2013-04-16 13:31:11
【问题描述】:

我的应用程序有大量对象,我将它们字符串化并将它们保存到磁盘。不幸的是,当数组中的对象被操作,有时被替换时,对象上的属性以不同的顺序列出(它们的创建顺序?)。当我对数组执行 JSON.stringify() 并保存它时,差异会显示以不同顺序列出的属性,这在尝试使用差异和合并工具进一步合并数据时很烦人。

理想情况下,我想在执行字符串化之前按字母顺序对对象的属性进行排序,或者作为字符串化操作的一部分。在许多地方都有用于操作数组对象的代码,并且更改这些代码以始终以明确的顺序创建属性是很困难的。

欢迎提出建议!

一个精简的例子:

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

这两个 stringify 调用的输出不同,并显示在我的数据差异中,但我的应用程序并不关心属性的顺序。对象的构造方式和位置很多。

【问题讨论】:

  • 请举例说明您要字符串化的对象(JSON 输出或 JS​​ 对象字面量)
  • 对象中的对象键不保证具有固定的顺序。这是设计使然。
  • @rab,你在哪里找到的?注意:它可以工作(并且可能大部分时间都可以),但我的意思是不能保证。
  • 在这里操作。这里不依赖于属性顺序,只是关于如何避免序列化数据中的差异的问题。这最终通过将属性存储在数组中并在序列化之前对其进行排序来解决,就像下面接受的答案一样。

标签: javascript json


【解决方案1】:

我不明白为什么需要当前最佳答案的复杂性来递归获取所有密钥。除非需要完美的性能,否则在我看来我们可以调用JSON.stringify() 两次,第一次获取所有密钥,第二次真正完成这项工作。这样一来,所有的递归复杂性都由stringify 处理,我们知道它知道自己的东西,以及如何处理每种对象类型:

function JSONstringifyOrder( obj, space )
{
    var allKeys = [];
    var seen = {};
    JSON.stringify(obj, function (key, value) {
        if (!(key in seen)) {
            allKeys.push(key);
            seen[key] = null;
        }
        return value;
    });
    allKeys.sort();
    return JSON.stringify(obj, allKeys, space);
}

【讨论】:

  • 这很有趣,但您实际上已经“欺骗”了使用 stringify 将所有键映射到数组。通过 Object.keys 可以更轻松、更好、更安全地获得该数组
  • 不,关键是Object.keys() 不是递归的,所以如果你有一个像{a: "A", b: {c: "C"} } 这样的对象并且你在它上面调用Object.keys,你只会得到键a 和@ 987654328@,但不是cJSON.stringify 了解 JSON 序列化过程处理的每种对象类型的递归,无论是类对象还是数组(它已经实现了识别两者的逻辑),因此它应该为我们正确处理所有事情。跨度>
  • 喜欢这个解决方案,看起来非常优雅且故障安全。
  • @Jor 谢谢,这对我有用。我尝试过的其他 sn-ps 以某种方式失败了。
  • 聪明!重新执行:我相信这是 O(N^2), N=对象中的数字键,因为对对象中的每个键线性检查排序键。因此,性能将在 GHz 机器上 N > 100K 左右开始成为明显的问题。对于 N > 100 万来说,性能可能会变得虚弱。参考:tc39.es/ecma262/#sec-json.stringify
【解决方案2】:

我刚刚重写了一个提到的示例,以便在 stringify 中使用它

const stringifySort = (key, value) => {
    if (!value || typeof value !== 'object' || Array.isArray(value)) return value;
    return Object.keys(value).sort().reduce((obj, key) => (obj[key]=value[key], obj), {});
};

JSON.stringify({name:"X", os:"linux"}, stringifySort);

【讨论】:

    【解决方案3】:

    更简单、现代且当前浏览器支持的方法就是这样:

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

    但是,此方法确实会删除任何未引用的嵌套对象,并且不适用于数组中的对象。如果你想要这样的输出,你也需要展平排序对象:

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

    你可以这样做:

    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;
    };
    var myFlattenedObj = flattenObject(sortMyObj);
    JSON.stringify(myFlattenedObj, Object.keys(myFlattenedObj).sort());
    

    要以您可以自己调整的方式以编程方式执行此操作,您需要将对象属性名称推送到一个数组中,然后按字母顺序对数组进行排序并遍历该数组(将按正确的顺序)并从中选择每个值按该顺序排列的对象。 “hasOwnProperty”也被选中,所以你肯定只有对象自己的属性。这是一个例子:

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

    同样,这应该保证您按字母顺序遍历。

    最后,以最简单的方式更进一步,这个库将允许您递归地对传入的任何 JSON 进行排序: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));
    

    输出

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

    【讨论】:

    • 这正是我试图避免的 :) 但是谢谢,这似乎是正确的解决方案,如果很重的话。
    • 这段代码有一个错误。您不能在 for 循环中引用 obj[i],因为 i 是一个整数,并且属性名称不一定(因此这个问题)。应该是obj[arr[i]]
    • 回调并不是异步行为独有的。
    • @Johann 通过在这里使用回调,他将iterateObjectAlphabetically 变成了一个可重用的函数。如果没有回调,“有效负载代码”必须在函数本身内部。我认为这是一个非常优雅的解决方案,并且在许多库和 JS 核心本身中都使用了它。例如。 Array.forEach.
    • 啊,我必须将那一点信息放在答案的下一句中!哦等等……
    【解决方案4】:

    不要与 Chrome 调试器的对象监控相混淆。它显示对象中的排序键,即使实际上它没有排序。在对对象进行字符串化之前,您必须对其进行排序。

    【讨论】:

    • 感谢您指出这一点。以上是针对 nodejs 应用程序的,但如果 Chrome 的调试器自己对事物进行排序,那么总有一天这对某人来说肯定是有用的信息。也许他们看到这个时会在这里发表评论:)
    【解决方案5】:

    这是一种克隆方法...在转换为 json 之前克隆对象:

    function sort(o: any): any {
        if (null === o) return o;
        if (undefined === o) return o;
        if (typeof o !== "object") return o;
        if (Array.isArray(o)) {
            return o.map((item) => sort(item));
        }
        const keys = Object.keys(o).sort();
        const result = <any>{};
        keys.forEach((k) => (result[k] = sort(o[k])));
        return result;
    }
    

    If 非常新,但似乎可以很好地处理 package.json 文件。

    【讨论】:

      【解决方案6】:

      毕竟,它需要一个数组来缓存嵌套对象中的所有键(否则它将忽略未缓存的键。)最早的答案是完全错误的,因为第二个参数不关心点符号。所以,答案(使用 Set)就变成了。

      function stableStringify (obj) {
        const keys = new Set()
        const getAndSortKeys = (a) => {
          if (a) {
            if (typeof a === 'object' && a.toString() === '[object Object]') {
              Object.keys(a).map((k) => {
                keys.add(k)
                getAndSortKeys(a[k])
              })
            } else if (Array.isArray(a)) {
              a.map((el) => getAndSortKeys(el))
            }
          }
        }
        getAndSortKeys(obj)
        return JSON.stringify(obj, Array.from(keys).sort())
      }
      

      【讨论】:

        【解决方案7】:

        由于某种原因,对于嵌套对象,接受的答案对我不起作用。这导致我自己编写代码。由于我写这篇文章时已是 2019 年末,因此该语言中还有更多可用选项。

        更新:我相信David Furlong's answer 是比我之前的尝试更可取的方法,我对此嗤之以鼻。我的依赖于对 Object.entries(...) 的支持,因此不支持 Internet Explorer。

        function normalize(sortingFunction) {
          return function(key, value) {
            if (typeof value === 'object' && !Array.isArray(value)) {
              return Object
                .entries(value)
                .sort(sortingFunction || undefined)
                .reduce((acc, entry) => {
                  acc[entry[0]] = entry[1];
                  return acc;
                }, {});
            }
            return value;
          }
        }
        
        JSON.stringify(obj, normalize(), 2);
        

        --

        保留此旧版本以供历史参考

        我发现对象中所有键的简单、扁平数组将起作用。 在几乎所有浏览器(不是 Edge 或 Internet Explorer,可以预见)和 Node 12+ 中都有一个相当短的解决方案现在Array.prototype.flatMap(...) 可用。 (lodash 等价物也可以。)我只在 Safari、Chrome 和 Firefox 中进行了测试,但我看不出它为什么不能在支持 flatMap 和标准 JSON.stringify(...) 的其他任何地方工作。

        function flattenEntries([key, value]) {
          return (typeof value !== 'object')
            ? [ [ key, value ] ]
            : [ [ key, value ], ...Object.entries(value).flatMap(flattenEntries) ];
        }
        
        function sortedStringify(obj, sorter, indent = 2) {
          const allEntries = Object.entries(obj).flatMap(flattenEntries);
          const sorted = allEntries.sort(sorter || undefined).map(entry => entry[0]);
          return JSON.stringify(obj, sorted, indent);
        }
        

        有了这个,您可以在没有第 3 方依赖项的情况下进行字符串化,甚至可以传入您自己的排序算法,该算法对键值条目对进行排序,因此您可以按键、有效负载或两者的组合进行排序。适用于嵌套对象、数组和普通旧数据类型的任何混合。

        const obj = {
          "c": {
            "z": 4,
            "x": 3,
            "y": [
              2048,
              1999,
              {
                "x": false,
                "g": "help",
                "f": 5
              }
            ]
          },
          "a": 2,
          "b": 1
        };
        
        console.log(sortedStringify(obj, null, 2));
        

        打印:

        {
          "a": 2,
          "b": 1,
          "c": {
            "x": 3,
            "y": [
              2048,
              1999,
              {
                "f": 5,
                "g": "help",
                "x": false
              }
            ],
            "z": 4
          }
        }
        

        如果您必须兼容较旧的 JavaScript 引擎,您可以使用这些稍微冗长的版本来模拟 flatMap 行为。客户端必须至少支持 ES5,因此不支持 Internet Explorer 8 或更低版本。

        这些将返回与上述相同的结果。

        function flattenEntries([key, value]) {
          if (typeof value !== 'object') {
            return [ [ key, value ] ];
          }
          const nestedEntries = Object
            .entries(value)
            .map(flattenEntries)
            .reduce((acc, arr) => acc.concat(arr), []);
          nestedEntries.unshift([ key, value ]);
          return nestedEntries;
        }
        
        function sortedStringify(obj, sorter, indent = 2) {
          const sortedKeys = Object
            .entries(obj)
            .map(flattenEntries)
            .reduce((acc, arr) => acc.concat(arr), [])
            .sort(sorter || undefined)
            .map(entry => entry[0]);
          return JSON.stringify(obj, sortedKeys, indent);
        }
        

        【讨论】:

          【解决方案8】:

          很惊讶没有人提到 lodash 的 isEqual 函数。

          在两个值之间进行深度比较以确定它们是否是 等价的。

          注意:此方法支持比较数组、数组缓冲区、布尔值、 日期对象、错误对象、地图、数字、对象对象、正则表达式、 集合、字符串、符号和类型化数组。比较对象对象 由它们自己的,而不是继承的,可枚举的属性。函数和 DOM 节点通过严格相等进行比较,即 ===。

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

          对于最初的问题 - 键的顺序不一致 - 这是一个很好的解决方案 - 当然,如果它发现冲突就会停止,而不是盲目地序列化整个对象。

          为避免导入整个库,请执行以下操作:

          import { isEqual } from "lodash-es";
          

          奖励示例: 你也可以通过这个自定义操作符将它与 RxJS 一起使用

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

          【讨论】:

            【解决方案9】:

            2018 年 7 月 24 日更新:

            此版本对嵌套对象进行排序并支持数组:

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

            测试用例:

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

            不推荐使用的答案:

            ES2016 中的简洁版本。 归功于@codename,来自https://stackoverflow.com/a/29622653/94148

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

            【讨论】:

            • 语法不太正确。应该是 - function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) =&gt; (r[k] = o[k], r), {})); }
            • 目前,这已经解决了我的问题,即 C# DataContractJsonSerializer 和“__type”未在 json 字符串中首先列出。谢谢。
            • 请注意,就像 Christian 的回答一样,嵌套对象行为不端。
            • sortObjPropertiesByKey 未定义。您的意思是使用 sortObjByKey 吗?
            • 这个sortObjByKey()似乎没有检查循环引用,所以要小心,它可能会根据输入数据陷入无限循环。示例:var objA = {}; var objB = {objA: objA}; objA.objB = objB; -> sortObjByKey(objA); -> VM59:6 Uncaught RangeError: Maximum call stack size exceeded
            【解决方案10】:

            扩展 AJP 的答案,以处理数组:

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

            【讨论】:

              【解决方案11】:

              我从@Jason Parham 那里得到了答案并做了一些改进

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

              这解决了数组被转换为对象的问题,它还允许您定义如何对数组进行排序。

              例子:

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

              输出:

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

              【讨论】:

                【解决方案12】:
                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 );
                }
                

                // 示例如下。在每一层中,对于像 {} 这样的对象,在键排序中展平。对于数组、数字或字符串,像/使用 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}"

                【讨论】:

                • 请解释为什么使用外部库,正如@DeKaNszn 所说,添加一些解释和介绍。将不胜感激。
                • 这个函数是从我的项目中复制而来的。我在其中使用 underscore.js。
                【解决方案13】:

                适用于 lodash、嵌套对象、对象属性的任何值:

                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)
                

                它依赖于 Chrome 和 Node 的行为,即分配给对象的第一个键首先由 JSON.stringify 输出。

                【讨论】:

                • 谢谢,但我在 4 年前就遇到过这个问题,我很高兴地说我从那以后有了一点进步 :)
                • :) 很高兴听到它。虽然我昨天遇到了这个问题,但在你的(旧但仍然相关的)问题下没有找到我正在寻找的答案:)
                【解决方案14】:

                如果列表中的对象没有相同的属性,则在字符串化之前生成一个组合的主对象:

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

                【讨论】:

                  【解决方案15】:

                  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;
                  

                  【讨论】:

                  • 这可能是最好的答案,谢谢@David
                  • 太棒了!可能会扩大检查:value instanceof Object &amp;&amp; !(value instanceof Array) &amp;&amp; !(value instanceof Date) &amp;&amp; !(value instanceof Function)
                  【解决方案16】:

                  Array.sort 方法可以为您提供帮助。例如:

                  yourBigArray.sort(function(a,b){
                      //custom sorting mechanism
                  });
                  

                  【讨论】:

                  • 我不希望对数组进行排序。实际上数组部分并不重要..我希望对对象的属性进行排序..从一些快速实验中,看起来属性是按照它们创建的顺序列出的。一种方法可能是创建一个新对象并按字母顺序复制属性,但我希望有一些更容易/更快的东西..
                  • @Innovine,即使这样也行不通,因为规范不保证按创建时间排序。
                  • 那么问题就变成了,我如何以这样一种方式对我的对象进行字符串化,以使键处于固定顺序.. 我不可能为 (key in obj) 做并将它们存储在一个临时数组并对其进行排序并手动构造一个字符串..但我非常希望有一种更简单的方法
                  • 不,这几乎就是这样做的方式。欢迎使用 JavaScript。
                  【解决方案17】:

                  您可以将属性名称的排序数组作为JSON.stringify() 的第二个参数传递:

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

                  【讨论】:

                  • 这种行为是否有任何形式的书面保证?
                  • @richremer 是的,在ECMAScript standard中,JSON.stringify()的第二个参数叫做replacer,可以是一个数组。它用于构建PropertyList,然后在SerializeJSONObject() 中使用以按该顺序附加属性。这是自 ECMAScript 5 以来的文档。
                  • 请注意,如果 obj 中有嵌套对象,则嵌套键也必须出现在第二个参数中;否则,他们将被丢弃!反例:&gt; JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}' 构建所有相关键列表的一种(hacky)方法是使用 JSON.stringify 遍历对象:function orderedStringify(obj) { const allKeys = []; JSON.stringify(obj, (k, v) =&gt; { allKeys.push(k); return v; }); return JSON.stringify(obj, allKeys.sort()); } 示例:&gt; orderedStringify({a: {c: 1, b: 2}}) '{"a":{"b":2,"c":1}}'
                  • Object.keys() 不是递归的。这不适用于任何包含嵌套数组或对象的对象。
                  【解决方案18】:

                  您可以在 EcmaScript 2015 中按属性名称对对象进行排序

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

                  【讨论】:

                  • 也许更好的名字是sortObjectPropertiesByName()或更简单的sortPropertiesByName()
                  【解决方案19】:

                  一个递归和简化的答案:

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

                  【讨论】:

                  • reorder 应该重命名为 sortObject 并且这不处理数组
                  • 感谢您注意到这一点,OP 的问题是处理对象,而不是数组。
                  • @JonathanGawrych 你为什么要对数组进行排序,它们应该有顺序。数组 [1,2,3] 与数组 [2,3,1] 不同,但对象没有属性顺序。问题是在字符串化之后顺序突然变得很重要——100% 等效对象的字符串可能不同。不幸的是,获得确定性字符串化的努力似乎相当大,示例包:github.com/substack/json-stable-stringify/blob/master/index.js
                  • @Mörre - 我想我不够具体。 “不处理数组”是指数组已损坏,而不是未排序。问题是因为typeof arr === "object" 会将数组转换为对象。例如var obj = {foo: ["bar", "baz"]} 将被字符串化为{ "foo": { "0": "bar", "1": "baz"} }
                  • 修复@JonathanGawrych 发现的问题:if(obj.constructor.prototype !== Object.prototype)
                  【解决方案20】:

                  我认为,如果您可以控制 JSON 的生成(听起来确实如此),那么出于您的目的,这可能是一个很好的解决方案:json-stable-stringify

                  来自项目网站:

                  具有自定义排序的确定性 JSON.stringify() 以获取 来自字符串化结果的确定性哈希

                  如果生成的 JSON 是确定性的,您应该能够轻松区分/合并它。

                  【讨论】:

                  • 请注意,该 repo 状况不佳:3 年内未更新,并且存在未解决的问题和拉取请求。我认为最好看看一些较新的答案。
                  • @Tom 另请注意,该 repo 每周有 500 万 (!) 次下载。换句话说,它正在被积极使用。拥有“未解决”的未解决问题和/或拉取请求并不意味着什么。只是作者不关心这些问题或没有时间等。并非每个库都需要每几个月更新一次。事实上,恕我直言,最好的库很少得到更新。当某事完成时,它就完成了。我并不是说这个项目没有真正的问题,但如果是这样,请指出这些。顺便说一句,我没有参与这个库。
                  • ES6 我猜它破坏了它能够按指定对对象(非数组)进行排序。我需要这个来进行用户编辑。即使命令对计算机无关紧要。它对编辑它的用户有用。值得一试。
                  • 除了@Tom 所说的,我还要指出这个项目没有运行时依赖。这意味着如果此 repo 中没有安全/维护问题,那么它可能非常稳定且使用安全。另外,我刚刚用 ES6 对其进行了测试,它似乎工作正常(至少在 Firefox 中)。
                  【解决方案21】:

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

                  【讨论】:

                    【解决方案22】:

                    我做了一个函数来对对象进行排序,并使用回调 .. 它实际上创建了一个新对象

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

                    并用对象调用它。

                    var obj = {
                        name : "anu",
                        os : "windows",
                        value : 'msio',
                    };
                    
                    var result = sortObj( obj , function( a, b ){
                        return a.key < b.key  ;    
                    });
                    
                    JSON.stringify( result )
                    

                    打印 {"value":"msio","os":"windows","name":"anu"} ,并按值排序。

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

                    打印{"os":"windows","value":"msio","name":"anu"}

                    【讨论】:

                    • 效果很好,但不幸的是它不是递归的。
                    【解决方案23】:

                    您可以将自定义toJSON 函数添加到您的对象,您可以使用它来自定义输出。在函数内部,以特定顺序将当前属性添加到新对象应在字符串化时保留该顺序。

                    看这里:

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

                    没有用于控制排序的内置方法,因为 JSON 数据旨在通过键访问。

                    这是一个带有小例子的 jsfiddle:

                    http://jsfiddle.net/Eq2Yw/

                    尝试注释掉 toJSON 函数 - 属性的顺序颠倒了。请注意,这可能是特定于浏览器的,即规范中不正式支持订购。它适用于当前版本的 Firefox,但如果您想要一个 100% 健壮的解决方案,您可能必须编写自己的字符串化函数。

                    编辑:

                    另请参阅有关 stringify 的非确定性输出的 SO question,尤其是 Daff 关于浏览器差异的详细信息:

                    How to deterministically verify that a JSON object hasn't been modified?

                    【讨论】:

                    • 每个对象的属性都是已知的(主要是字符串),所以在我自己的 toJSON 字符串化器中硬编码属性可能比排序快得多...!
                    • 下面的 'orderedJsonStringify' 几乎起作用了。但这对我来说似乎是一个更好的解决方案。当我需要将 C# DataContractJsonSerializer 的“__type”作为 json 字符串中的第一项时。 var json = JSON.stringify(myjsonobj, ['__type', 'id', 'text', 'somesubobj' etc.....]);列出所有键有点痛苦。
                    【解决方案24】:

                    试试:

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

                    【讨论】:

                    • 谢谢,但显然排序是未定义的,只是碰巧起作用。有趣的方法!
                    猜你喜欢
                    • 2019-01-30
                    • 2013-12-28
                    • 1970-01-01
                    • 2016-02-08
                    • 1970-01-01
                    • 2011-01-28
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-04-26
                    相关资源
                    最近更新 更多