【问题标题】:Getting a diff of two json-objects获取两个 json 对象的差异
【发布时间】:2012-01-15 22:17:08
【问题描述】:

场景:我想要一个比较两个 JSON 对象的函数,并返回一个带有差异列表的 JSON 对象,如果可能的话,还需要更多数据,例如覆盖率指标。

var madrid = '{"type":"team","description":"Good","trophies":[{"ucl":"10"}, {"copa":"5"}]}';
var barca = '{"type":"team","description":"Bad","trophies":[{"ucl":"3"}]}';

如果我运行compare(madrid, barca),返回的对象可能类似于:

{"description" : "Bad", "trophies":[{"ucl":"3"}, {"copa":"5"}]}; 

或者类似的东西,你懂的。

有人知道这个问题的解决方案吗?我已经找到了一个plugin,但我想知道是否有其他选择。

【问题讨论】:

  • 当您说“JSON 对象”时,您真的只是指“对象”,还是您实际上拥有 JSON(字符串)形式的数据?另外,您能否提供有关预期输出的更多详细信息?差异可能意味着“在 A 但不是 B”、“在 B 但不是 A”或“在两者中但不同”。您希望如何报告唯一不同是向下几个级别的嵌套对象?
  • 我已经用更多信息更新了最初的帖子!

标签: javascript jquery json diff


【解决方案1】:

我们可以使用 JSON.Stringify

var obj1 = {
  "Agent": "12819",
  "Beneficiary": "476949",
  "BillingDetail": "13772",
  "BillingInvoice": "914548",
  "Claim": "1192",
  "Customer": "656320",
  "LifeAssured": "228493",
  "Payment": "1091661",
  "Policy/Proposal": "263196",
  "Product": "9",
  "ProductComponent": "53",
  "ProductComponentOption": "2239791",
  "ProductOption": "568785",
  "TransactionDetail": "4289240"
}

var obj2 = {
  "Agent": "1289",
  "Beneficiary": "47694",
  "BillingDetail": "13772",
  "BillingInvoice": "914548",
  "Claim": "1192",
  "Customer": "656320",
  "LifeAssured": "22893",
  "Payment": "1091661",
  "Policy/Proposal": "26316",
  "Product": "2",
  "ProductComponent": "52",
  "ProductComponentOption": "223971",
  "ProductOption": "56885",
  "TransactionDetail": "4289240"
}

JSON.stringify(obj1) == JSON.stringify(obj2)
false

var obj3 = {
  "Agent": "1289",
  "Beneficiary": "47694",
  "BillingDetail": "13772",
  "BillingInvoice": "914548",
  "Claim": "1192",
  "Customer": "656320",
  "LifeAssured": "22893",
  "Payment": "1091661",
  "Policy/Proposal": "26316",
  "Product": "2",
  "ProductComponent": "52",
  "ProductComponentOption": "223971",
  "ProductOption": "56885",
  "TransactionDetail": "4289240"
}

JSON.stringify(obj2) == JSON.stringify(obj3)
true

【讨论】:

  • 此解决方案对我不起作用,因为在我的情况下字段的顺序不同。理想情况下,在比较 JSON 时,顺序应该无关紧要。
【解决方案2】:

将我的更改贡献给 Gabriel Gartz 版本。这个在严格模式下工作并删除数组检查 - 将始终为假。它还会从 diff 中删除空节点。

//http://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object
var isEmptyObject = function(obj) {
    var name;
    for (name in obj) {
        return false;
    }
    return true;
};

//http://stackoverflow.com/questions/8431651/getting-a-diff-of-two-json-objects
var diff = function(obj1, obj2) {
    var result = {};
    var change;
    for (var key in obj1) {
        if (typeof obj2[key] == 'object' && typeof obj1[key] == 'object') {
            change = diff(obj1[key], obj2[key]);
            if (isEmptyObject(change) === false) {
                result[key] = change;
            }
        }
        else if (obj2[key] != obj1[key]) {
            result[key] = obj2[key];
        }
    }
    return result;
};

【讨论】:

  • 好多了。但是为什么不使用 getOwnPropertyNames() 而不是 for..in? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 抱歉,我试过了,认为它需要更多的工作。最大的问题是如果你交换对象参数的顺序,结果应该是一样的。你不应该能够在两个对象之间有“不同”的差异。此外,它似乎专注于检测值差异,但在许多情况下,添加/删除了哪些属性很重要。但是再次感谢您的工作。示例:这两个调用应该返回相同的结果:console.log(diff({a: 1}, {a: 10, b: 2, c: 3})) console.log(diff({a: 10, b : 2, c: 3}, {a: 1}))
【解决方案3】:

可以使用通过对象键迭代的递归函数。然后使用Object.is 测试NaNnull。然后测试第二个对象是否是转换为false 的类型,如0NaNnull。 列出两个对象的键并将它们连接起来以测试obj1 中缺少的键,然后对其进行迭代。

当相同键值存在差异时,存储object2的值并继续。如果两个键值都是对象,则意味着可以递归比较,因此可以。

function diff(obj1, obj2) {
    const result = {};
    if (Object.is(obj1, obj2)) {
        return undefined;
    }
    if (!obj2 || typeof obj2 !== 'object') {
        return obj2;
    }
    Object.keys(obj1 || {}).concat(Object.keys(obj2 || {})).forEach(key => {
        if(obj2[key] !== obj1[key] && !Object.is(obj1[key], obj2[key])) {
            result[key] = obj2[key];
        }
        if(typeof obj2[key] === 'object' && typeof obj1[key] === 'object') {
            const value = diff(obj1[key], obj2[key]);
            if (value !== undefined) {
                result[key] = value;
            }
        }
    });
    return result;
}

上面的代码是 BSD 许可的,可以在任何地方使用。

测试链接:https://jsfiddle.net/gartz/vy9zaof2/54/

一个重要的观察,这会将数组转换为对象并比较相同索引位置的值。由于所需的额外复杂性,还有许多其他方法可以比较此函数未涵盖的数组。

编辑 2/15/2019:此答案已更改为添加新的 ES2017 语法并修复 cmets 的用例。


这只是一个开始,我还没有测试过,但是我从一个过滤器或比较器函数开始,它是递归的,根据需要更改它以获得优先结果。

function filter(obj1, obj2) {
    var result = {};
    for(key in obj1) {
        if(obj2[key] != obj1[key]) result[key] = obj2[key];
        if(typeof obj2[key] == 'array' && typeof obj1[key] == 'array') 
            result[key] = arguments.callee(obj1[key], obj2[key]);
        if(typeof obj2[key] == 'object' && typeof obj1[key] == 'object') 
            result[key] = arguments.callee(obj1[key], obj2[key]);
    }
    return result;
}

测试:http://jsfiddle.net/gartz/Q3BtG/2/

【讨论】:

  • 感谢您的回复。我试过了,但我不能真正利用返回的对象。 jsfiddle.net/zorro/Q3BtG
  • Emil,我的函数正在工作我在 jsfiddle 中测试过,你输入了 var 作为字符串而不是对象,如果你删除第一个和最后一个 ' 它将按预期工作。并修复 arguments.callee 代替 arguments.calee jsfiddle.net/gartz/Q3BtG/2
  • 似乎有一个错误,如果 json 中的值具有空值。然后有一个空的json对象作为更改的元素
  • 我使用 ES6 更新了这个示例并删除了 argument.callee 语句,如果有人发现它有用的话:jsfiddle.net/thinkolson/n2jwv6mk/2
  • 失败了,试试例子:var jsonObject1 = { name: 'my object', description: 'it\'s an object!', lead:[{"user_id":8,"is_active" :1}] }; var jsonObject2 = { name: 'my objectd', description: '它是一个对象!', lead:[{"user_id":8,"is_active":1}] };
【解决方案4】:

您可以使用 rus-diff https://github.com/mirek/node-rus-diff 创建与 MongoDB 兼容的(重命名/取消设置/设置)差异:

// npm install rus-diff
var madrid = {"type":"team","description":"Good","trophies":[{"ucl":"10"}, {"copa":"5"}]};
var barca = {"type":"team","description":"Bad","trophies":[{"ucl":"3"}]};
var rusDiff = require('rus-diff').rusDiff
console.log(rusDiff(madrid, barca))

输出:

{ '$unset': { 'trophies.1': true },
  '$set': { description: 'Bad', 'trophies.0.ucl': '3' } }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-12
    • 1970-01-01
    • 2019-12-31
    • 1970-01-01
    • 1970-01-01
    • 2012-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多