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

str*_*ing 7 javascript json checksum adler32

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

var obj1 = {type:"cake",quantity:0}
  , obj2 = {quantity:0,type:"cake"};
Run Code Online (Sandbox Code Playgroud)

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

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

运行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
}
Run Code Online (Sandbox Code Playgroud)

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

但是,我只是想知道我是否遗漏了某些东西并且有一个内置的方法,这样的事情一直没有a)驱动JavaScript GC进入病态案例或b)做太多的字符串连接.我宁愿不做那些.

jfr*_*d00 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);
}
Run Code Online (Sandbox Code Playgroud)

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


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


Fos*_*sco 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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 不,它没有,因为它只使用`akeys`. (2认同)
  • 老兄..认真的.退后一步.你错过了什么.订单与此处无关,并且不涉及现代浏览器技巧.这种类型完全,毫不含糊,没有必要.检查密钥的顺序无关紧要,只检查它们.按键从A键中键,确保该键的值在A和B上相同.足够. (2认同)
  • 没有.不,我根本没有假设.我不在乎他们回归的顺序.你完全误读了这里发生的事情,应该停下来. (2认同)

Yan*_*oto 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
}
Run Code Online (Sandbox Code Playgroud)

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


请注意这里的许可证