如何从JavaScript对象创建校验和?

时间:2014-07-23 17:32:56

标签: javascript json checksum adler32

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

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

我认为这些对象的数据相同,并希望它们的校验和相同。只要其中的数据相同,我就不在乎对象的顺序 唉,他们两个JSON.stringify实际上并不相同;作为对象的校验和的唯一方法是通过其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),当然它会使两个对象字符串表示相等!

然而,我只是想知道我是否遗漏了某些东西,并且有一种内置的方法可以解决这样的问题,而不是将JavaScript GC驱动到病态案例中或b)做得太多字符串连接。 我宁愿不做那些。

3 个答案:

答案 0 :(得分: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;
}

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

答案 1 :(得分:3)

您可以将密钥收集到具有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);
}

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


如果您可以将嵌入对象作为属性值,那么还需要做一些工作来将这些对象重新扩展为相同的扁平属性/值数组。

答案 2 :(得分:2)

3年后......

我遇到了这个问题,因为我想哈希我的JSON对象来为我的HTTP响应创建Etag。所以我最终为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)获取结果并对其进行哈希处理,或者使用来自digest包的jsum方便的方法。

请注意许可证here