web-dev-qa-db-ja.com

2つのオブジェクトを「差分」できるJavaライブラリはありますか?

Unixプログラムのdiffに似ているJavaユーティリティライブラリがありますが、Objects用ですか?同じタイプの2つのオブジェクトを比較し、表すデータ構造を生成できるものを探していますそれらの違い(およびインスタンス変数の違いを再帰的に比較できます)。not Javaテキストdiffの実装を探しています。私も- notリフレクションを使用してこれを行う方法のヘルプを探しています。

私が管理しているアプリケーションには、この機能の脆弱な実装があり、設計の選択肢が不十分であり、書き換える必要がありますが、市販のものを使用できればさらに良いでしょう。

私が探している種類の例は次のとおりです。

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」が2つのオブジェクト間で異なり、「prop2」が同じであることを教えてくれます。 DiffDataStructureがツリーであるのは最も自然なことだと思いますが、コードの信頼性が高ければ、気難しいことにはなりません。

72
Kaypro II

少し遅れるかもしれませんが、私はあなたと同じ状況にあり、まさにあなたのユースケースに合わせて自分のライブラリを作成することになりました。私は自分で解決策を考え出さなければならなかったので、他の人の苦労を省くためにGithubでリリースすることにしました。ここにあります: https://github.com/SQiShER/Java-object-diff

---Edit---

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;
36
SQiShER

http://javers.org は、必要なものを正確に実行するライブラリです。diffオブジェクトを返す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シリアライザーとデシリアライザーがあり、データベースに簡単に保存できるように変更されています。このライブラリを使用すると、監査用のモジュールを簡単に実装できます。

29
Paweł Szymczyk

はい、 Java-util ライブラリには、2つのJavaオブジェクトグラフを比較するGraphComparatorクラスがあります。差分のリストとして差分を返します。GraphComparatorでは、デルタもマージ(適用)しますこのコードはJDKにのみ依存し、他のライブラリには依存しません。

9

すべてのJaversライブラリはJava 7のみをサポートしています。これをJava 6プロジェクトに使用したいので、ソースを取得して、Java 6が機能するように変更します。以下の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にサポートしたことです。私にとって不要なコードは、すべてのカスタムオブジェクトの比較で機能します。

3
Sandesh

このコードは、このコードを使用する場所によっては役立つ場合がありますが、役に立つ場合や問題がある場合があります。このコードをテストしました。

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

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

0
John Blackwell