是否存在可以“区分”两个对象的Java库?


86

是否有一个类似于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

比较之后,该实用程序将告诉我两个对象之间的“ prop1”不同,并且“ prop2”相同。我认为DiffDataStructure成为一棵树是最自然的,但是如果代码可靠,我也不会挑剔。


8
检查一下,code.google.com
p / jettison


13
这与“如何测试复杂对象图的相等性?”不是重复的。我正在寻找一个图书馆,而不是关于如何做的建议。此外,我对增量感兴趣,而不是相等或不相等。
Kaypro II

1
看看github.com/jdereg/java-util具有GraphComparator工具。此类将生成对象图之间的增量列表。另外,它可以将增量合并(应用)到图形。实际上,它是List <Delta> = GraphComparator(rootA,rootB)。以及GraphComparator.applyDelta(rootB,List <Delta>)。
John DeRegnaucourt '16

我知道您不想使用反射,但这绝对是实现这样的东西的最简单/简单的方法
RobOhRob

Answers:


42

可能会晚一点,但是我和您一样处于同样的情况,最终为您的用例创建了自己的库。由于我自己不得不提出解决方案,因此我决定在Github上发布该解决方案,以免其他人辛苦工作。您可以在这里找到它:https : //github.com/SQiShER/java-object-diff

-编辑-

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

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;

1
我可以指出这篇文章,它显示一个java-object-diff用例吗?stackoverflow.com/questions/12104969/…非常感谢这个有用的库!
Matthias Wuttke,2012年

我看了一下您的帖子,并写了一个答案,结果竟然太长了,无法发表为评论,所以这是它的“要点”:gist.github.com/3555555
SQiShER 2012年

2
整齐。我最终也写了自己的实现。我不确定是否有机会真正探索您的实现,但是我对您如何处理列表感到好奇。我最终将所有集合差异作为Maps的差异进行处理(通过以不同的方式定义键(取决于它是列表,集合还是映射;然后在keySet上使用set操作))。但是,该方法对列表索引更改过于敏感。我没有解决该问题,因为我不需要我的应用程序,但是我很好奇您如何处理它(我想更像是文本差异)。
Kaypro II

2
您提出了一个好点。收集真的很难处理。特别是如果您像我一样支持合并。我通过使用对象标识来检测是否已添加,更改或删除项(通过hashCode,等于和包含)来解决该问题。这样做的好处是,我可以将所有集合视为相同。不好的是,我无法正确处理(例如)多次包含相同对象的ArrayList。我很乐意为此添加支持,但是它似乎非常复杂,幸运的是,还没有人要求它。:-)
SQiShER 2012年

丹尼尔,感谢您的评论!没错,很难从差异中重建原始对象,但是在我看来这并不是太重要。最初,我按照您的建议将对象的早期版本存储在数据库中。问题在于大型文档的大部分没有更改,有很多重复的数据。这就是为什么我开始寻找替代品并找到java-object-diff的原因。我在收集/映射方面也遇到了困难,并更改了“等于”方法来检查数据库主数据库(而不是整个对象)是否相等,这有所帮助。
Matthias Wuttke

39

http://javers.org是一个库,它完全满足您的需求:具有compare(Object leftGraph,Object rightGraph)之类的方法返回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

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

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


1
这行不通。如何创建Javers实例?
Ryan Vettese 2014年

2
Javers具有出色的文档,解释一切:javers.org
帕维尔Szymczyk

2
我尝试使用它,但仅适用于Java> = 7。伙计们,仍然有人在Java 6上从事项目!!!
jesantana

2
如果我比较的两个对象内部都有一个对象列表,那我也会看到那些差异吗?javers可以比较的层次结构是什么?
Deepak

1
是的,您会看到内部对象的差异。目前,没有层次结构级别的阈值。Javers会尽可能深入,限制是堆栈大小。
帕维尔Szymczyk

15

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


12
似乎是一个不错的图书馆,您应该提及自己是一名撰稿人
Christos

3

所有Javers库仅支持Java 7,由于我希望将其用于Java 6项目,因此我处于这种情况,所以我碰巧采用了源代码并更改了它对Java 6的工作方式,以下是github代码。

https://github.com/sand3sh/javers-forJava6

Jar链接:https//github.com/sand3sh/javers-forJava6/blob/master/build/javers-forjava6.jar

我只将Java 7支持的'<>'固有转换转换更改为Java 6支持。我不保证所有功能都可以使用,因为我为我注释了一些不必要的代码,该代码可用于所有自定义对象比较。




1

也许会有所帮助,具体取决于您在哪里使用此代码,它可能有用或有问题。测试了此代码。

    /**
 * @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();
        }
}

2
谢谢,但是我们已经有了使用反射遍历对象图的代码,列出了更改。这个问题不是关于如何做,而是关于避免重新发明轮子的问题。
Kaypro II

如果重新发明已经发生,那么重新发明轮子也不错。(即:重新设计的车轮已经付款。)
Thomas Eding

1
我同意你的看法,但是在这种情况下,重新编写的代码是无法维持的混乱。
Kaypro II

3
我会检查Fields不是方法。方法可以是任何东西,例如getRandomNumber()
波希米亚

-3

一种快速告诉您两个对象是否不同的简单方法是使用apache commons库

    BeanComparator lastNameComparator = new BeanComparator("lname");
    logger.info(" Match "+bc.compare(firstInstance, secondInstance));

这对我不起作用,因为我需要知道发生了什么变化,而不仅是某处发生了变化。
Kaypro II
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.