是否有可以"区分"两个对象的Java库?

Kay*_* II 77 java diff

是否存在类似于Unix程序diff的Java实用程序库,但对于Objects?我正在寻找可以比较两个相同类型的对象并生成表示它们之间差异的数据结构的东西(并且可以递归地比较实例变量中的差异).我不是在寻找文本差异的Java实现.我也没有寻求有关如何使用反射来做到这一点的帮助.

我正在维护的应用程序有一个脆弱的实现这个功能,有一些糟糕的设计选择,需要重写,但如果我们可以使用现成的东西会更好.

这是我正在寻找的那种事情的一个例子:

SomeClass a = new SomeClass();
SomeClass b = new SomeClass();

a.setProp1("A");
a.setProp2("X");

b.setProp1("B");
b.setProp2("X");

DiffDataStructure diff = OffTheShelfUtility.diff(a, b);  // magical recursive comparison happens here
Run Code Online (Sandbox Code Playgroud)

比较之后,实用程序会告诉我"prop1"在两个对象之间是不同的,"prop2"是相同的.我认为DiffDataStructure最自然的是树,但如果代码可靠,我不会挑剔.

SQi*_*hER 37

可能会有点晚,但我和你一样处于同样的境地,并最终为你的用例创建了我自己的库.由于我自己被迫提出解决方案,我决定在Github上发布它,以免其他人付出辛苦的努力.你可以在这里找到它:https://github.com/SQiShER/java-object-diff

--- 编辑 ---

这是一个基于OP代码的小用法示例:

SomeClass a = new SomeClass();
SomeClass b = new SomeClass();

a.setProp1("A");
a.setProp2("X");

b.setProp1("B");
b.setProp2("X");

DiffNode diff = ObjectDifferBuilder.buildDefault().compare(a, b);

assert diff.hasChanges();
assert diff.childCount() == 1;
assert diff.getChild('prop1').getState() == DiffNode.State.CHANGED;
Run Code Online (Sandbox Code Playgroud)

  • 整齐.我最后也编写了自己的实现.我不确定我是否有机会真正探索你的实现,但我很好奇你如何处理列表.我最终将所有集合差异视为映射的差异(通过以不同方式定义键,具体取决于它是列表,集合还是映射;然后在keySet上使用set操作).但是,该方法对列表索引更改过于敏感.我没有解决这个问题,因为我不需要为我的应用程序,但我很好奇你是如何处理它的(更像是text-diff,我假设). (2认同)
  • 你提出了一个很好的观点.收藏品很难处理.特别是如果你支持合并,就像我一样.我通过使用对象标识来检测是否已添加,更改或删除项目(通过hashCode,equals和contains)来解决问题.关于这一点的好处是,我可以对待所有收藏品.不好的是,我无法正确处理(例如)多次包含同一对象的ArrayLists.我很乐意为此添加支持,但它似乎相当复杂,幸运的是没有人要求它.:-) (2认同)

Paw*_*zyk 31

http://javers.org是一个可以满足你需要的库:像compare(Object leftGraph,Object rightGraph)这样的方法返回Diff对象.Diff包含更改列表(ReferenceChange,ValueChange,PropertyChange),例如

given:
DummyUser user =  dummyUser("id").withSex(FEMALE).build();
DummyUser user2 = dummyUser("id").withSex(MALE).build();
Javers javers = JaversTestBuilder.newInstance()

when:
Diff diff = javers.compare(user, user2)

then:
diff.changes.size() == 1
ValueChange change = diff.changes[0]
change.leftValue == FEMALE
change.rightValue == MALE
Run Code Online (Sandbox Code Playgroud)

它可以处理图形中的循环.

此外,您还可以获取任何图形对象的快照.Javers有JSON序列化器和反序列化器来快照和更改,因此您可以轻松地将它们保存在数据库中.使用此库,您可以轻松实现用于审核的模块.

  • Javers拥有出色的文档,可以解释所有内容:[javers.org](http://javers.org/documentation/getting-started/#create-javers-instance) (2认同)
  • 我尝试使用它,但仅适用于Java> = 7.伙计们,仍然有人在Java 6上开展项目! (2认同)
  • 如果我比较的两个对象内部有一个对象列表,我也会看到这些差异怎么办?javers可以比较的层次结构是什么? (2认同)

Joh*_*urt 10

是的,java-util库有一个GraphComparator类,它将比较两个Java对象图.它将差异作为增量列表返回.GraphComparator还允许您合并(应用)增量.此代码仅依赖于JDK,没有其他库.

  • 看起来很漂亮的图书馆,你应该提到你是一个贡献者 (6认同)

小智 9

您还可以查看 Apache 的解决方案。自从它是 commons-lang 的一部分以来,大多数项目已经在他们的类路径上有了它。

检查特定字段的差异:http :
//commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/DiffBuilder.html

通过使用反射检查差异:http :
//commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/ReflectionDiffBuilder.html


r0a*_*t3d 2

也许这会有所帮助,具体取决于您使用此代码的位置,它可能有用或有问题。测试了这段代码。

    /**
 * @param firstInstance
 * @param secondInstance
 */
protected static void findMatchingValues(SomeClass firstInstance,
        SomeClass secondInstance) {
    try {
        Class firstClass = firstInstance.getClass();
        Method[] firstClassMethodsArr = firstClass.getMethods();

        Class secondClass = firstInstance.getClass();
        Method[] secondClassMethodsArr = secondClass.getMethods();


        for (int i = 0; i < firstClassMethodsArr.length; i++) {
            Method firstClassMethod = firstClassMethodsArr[i];
            // target getter methods.
            if(firstClassMethod.getName().startsWith("get") 
                    && ((firstClassMethod.getParameterTypes()).length == 0)
                    && (!(firstClassMethod.getName().equals("getClass")))
            ){

                Object firstValue;
                    firstValue = firstClassMethod.invoke(firstInstance, null);

                logger.info(" Value "+firstValue+" Method "+firstClassMethod.getName());

                for (int j = 0; j < secondClassMethodsArr.length; j++) {
                    Method secondClassMethod = secondClassMethodsArr[j];
                    if(secondClassMethod.getName().equals(firstClassMethod.getName())){
                        Object secondValue = secondClassMethod.invoke(secondInstance, null);
                        if(firstValue.equals(secondValue)){
                            logger.info(" Values do match! ");
                        }
                    }
                }
            }
        }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
}
Run Code Online (Sandbox Code Playgroud)

  • 我会检查“Fields”而不是方法。方法可以是任何东西,例如“getRandomNumber()”。 (3认同)
  • 谢谢,但是我们已经有了使用反射列出更改来遍历对象图的代码。这个问题不是关于如何做,而是关于试图避免重新发明轮子。 (2认同)