如何确定性地验证JSON对象是否未被修改?

aw *_*rud 37 javascript json

根据JSON.stringify的MDN文档:

不保证非数组对象的属性按任何特定顺序进行字符串化.不要依赖于字符串化内同一对象内的属性排序.

我曾希望通过缓存对象的字符串化版本来确定对象是否已更改,然后将其与随后的字符串化对象版本进行比较.这似乎比递归迭代对象并进行比较要简单得多.问题是因为JSON.stringify函数不是确定性的,所以当我对同一个对象进行字符串化时,我可以在技术上得到一个不同的字符串.

我还有其他选择吗?或者我是否必须编写令人讨厌的比较函数来确定对象相等性?

Daf*_*aff 9

我很确定这是因为不同的JavaScript引擎在内部跟踪对象属性的方式.以此为例:

var obj = {
"1" : "test",
"0" : "test 2"
};

for(var key in obj) {
    console.log(key);
}
Run Code Online (Sandbox Code Playgroud)

这将在例如Firefox中记录1,0,但在V8(Chrome和NodeJS)中记录0,1.因此,如果您需要确定性,您可能必须遍历数组中的每个密钥存储,对数组进行排序,然后通过循环遍历该数组来单独对每个属性进行字符串化.


Tho*_*asR 9

你可能想尝试JSON.sortify,我写的一个小帮手.

与迄今为止给出的答案相反,它

  • 适用于任何级别的嵌套
  • 可以处理数字键
  • 转义键中的特殊字符
  • 接受space参数以及很少使用的replacer参数
  • 在循环引用上抛出TypeError(应该如此)
  • 过滤undefined价值和功能
  • 尊重 toJSON()

  • @hippietrail之前我见过,你当然可以使用它.主要的艺术建筑差异是JSON.sortify尝试实现与JSON.stringify的最大兼容性,即您可以简单地覆盖`JSON.stringify = JSON.sortify`并确保没有任何中断.json-stable-stringify不是这种情况.例如,它没有提供[replacer参数](http://www.dyn-web.com/tutorials/php-js/json/filter.php),它会在循环值等时崩溃. (2认同)

Jim*_*eis 5

下面是一个确定性的JSON.stringify(),我写的(使用的实现Underscore.js).它以递归方式将(非数组)对象转换为已排序的键值对(如数组),然后对这些对象进行字符串化.原始的coderwall发布在这里.

字符串化:

function stringify(obj) {
  function flatten(obj) {
    if (_.isObject(obj)) {
      return _.sortBy(_.map(
          _.pairs(obj),
          function(p) { return [p[0], flatten(p[1])]; }
        ),
        function(p) { return p[0]; }
      );
    }
    return obj;
  }
  return JSON.stringify(flatten(obj));
}
Run Code Online (Sandbox Code Playgroud)

解析:

function parse(str) {
  function inflate(obj, pairs) {
     _.each(pairs, function(p) {
      obj[p[0]] = _.isArray(p[1]) ?
        inflate({}, p[1]) :
        p[1];
    });
    return obj;
  }
  return inflate({}, JSON.parse(str));
}
Run Code Online (Sandbox Code Playgroud)

  • 但要注意,使用上面的stringify,`stringify({0:"a"})== stringify(["a"])`,这有点缺陷. (2认同)
  • 请注意,这不仅仅是顶层的数组,而是任意深度的任何数组.例如,请注意:`stringify({a:{0:"a"}})== stringify({a:["a"]})`.因此,如果数据结构在任何地方使用数组,我将能够构造一个非数组结构,给出相同的`stringify`字符串. (2认同)