9

我需要从 JavaScript 对象进行校验和。
不幸的是,由于 JavaScript 的对象排序,似乎没有一种简单的方法可以实现这一点。例如,采取这些对象:

var obj1 = {type:"cake",quantity:0}
  , obj2 = {quantity:0,type:"cake"};

我认为这些对象的数据相等,并希望它们的校验和相同。只要对象中的数据相同,我真的不在乎对象的顺序。
唉,JSON.stringify这两者实际上并不相等;由于对 Object 进行校验和的唯一方法是通过其 String 表示,并且JSON.stringify-ed 表示不相等,因此我的校验和将不相等!
我想出的一种解决方案是根据预定义的模式重新创建对象,如下所示:

var schema = ["type","quantity"];
function sortify(obj,schema){
  var n={};
  for(i in schema)
    n[schema[i]]=obj[schema[i]];
  return n
}

运行JSON.stringify(sortify(obj1,schema))==JSON.stringify(sortify(obj2,schema))将返回true......但代价是创建一个新对象并在数据周围洗牌。

我的另一个解决方案是将该JSON.stringify方法替换为从预定义模式中选择键并将它们的值字符串化,然后将它们连接在一起的方法。函数内容如下:

function smarterStringify(obj,schema){
  var s="";
  for(i in schema)
    s+=JSON.stringify(obj[schema[i]]);
  return s
}

忽略这个方法没有返回正确的 JSON 的事实(它作为我正在尝试做的一个例子已经足够接近),它在速度上比第一个方法有了很大的改进(至少在我的 Chrome OS 浏览器中,你可以在这里自己检查:http: //jsperf.com/sort-then-json-stringify-vs-smarter-stringify),当然它使两个对象字符串表示相等!

然而,我只是想知道我是否遗漏了一些东西,并且有一个内置的方法来处理这样的事情,它没有 a) 将 JavaScript GC 驱动到一个病态的情况下,或者 b) 做了太多的字符串连接。我宁愿不做那些。

4

3 回答 3

6

您可以使用 将键收集到一个数组中Object.keys(),对该数组进行排序,然后以已知的、可预测的顺序对键/值进行校验和。我不知道有什么方法可以同时使用JSON.stringify()所有排序的键,所以你必须自己做校验和。

我不知道有任何类似这样的内置方法。不保证对象键按任何特定顺序排列,因此依赖它是不安全的。


如果您没有嵌套对象或数组作为属性值,那么您可以执行以下操作:

// creates an array of alternating property name, property value
// with properties in sorted order
// then stringify's that array
function stringifyPropsInOrder(obj) {
    var keys = Object.keys(obj).sort();
    var output = [], prop;
    for (var i = 0; i < keys.length; i++) {
        prop = keys[i];
        output.push(prop);
        output.push(obj[prop]);
    }
    return JSON.stringify(output);
}

function compareObjects(a, b) {
    return stringifyPropsInOrder(a) === stringifyPropsInOrder(b);
}

如果您想要更快的性能,则不必进行字符串化(这里只是为了保存代码而完成的)。您可以只返回展平output的数组并直接比较数组。


如果您可以将嵌入对象作为属性值,则需要做更多的工作才能将它们递归地扩展为相同的扁平属性/值数组。

于 2014-07-23T17:35:09.513 回答
5

您还可以创建一个函数来比较您的对象:

function compareObjects(a, b) {
  var akeys = Object.keys(a);
  var bkeys = Object.keys(b);
  var len = akeys.length;
  if (len != bkeys.length) return false;
  for (var i = 0; i < len; i++) {
    if (a[akeys[i]] !== b[akeys[i]]) return false;
  }
  return true;
}

这假设它们是传入的对象,并且它们是简单的平面对象。可以添加逻辑来检查这些假设并递归检查子对象是否相等。

于 2014-07-23T17:50:13.653 回答
5

3年后...

我遇到了这个问题,因为我想对我的 JSON 对象进行哈希处理,以便Etag为我的 HTTP 响应创建 s。所以我最终为 Node 编写了自己的解决方案,jsum归结为一个简单的序列化程序:

/**
 * Stringifies a JSON object (not any randon JS object).
 *
 * It should be noted that JS objects can have members of
 * specific type (e.g. function), that are not supported
 * by JSON.
 *
 * @param {Object} obj JSON object
 * @returns {String} stringified JSON object.
 */
function serialize (obj) {
  if (Array.isArray(obj)) {
    return JSON.stringify(obj.map(i => serialize(i)))
  } else if (typeof obj === 'object' && obj !== null) {
    return Object.keys(obj)
      .sort()
      .map(k => `${k}:${serialize(obj[k])}`)
      .join('|')
  }

  return obj
}

然后,您可以使用常用算法(例如SHA256)或使用digestfrom jsumpackage 的便捷方法获取结果并对其进行哈希处理。


请注意这里的许可证!

于 2017-01-27T11:09:22.437 回答