我需要从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)做得太多字符串连接。 我宁愿不做那些。
答案 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 !