web-dev-qa-db-fra.com

Existe-t-il une bibliothèque Java qui peut "différencier" deux objets?

Existe-t-il une bibliothèque d'utilitaires Java similaire au programme Unix, mais pour les objets? Je cherche quelque chose qui puisse comparer deux objets du même type et générer une structure de données qui représente les différences entre eux (et peut comparer récursivement les différences dans les variables d'instance). Je suis pas à la recherche d'un Java implémentation d'un texte diff. Je suis aussi - pas chercher de l'aide sur la façon d'utiliser la réflexion pour ce faire.

L'application que je gère a une implémentation fragile de cette fonctionnalité qui avait de mauvais choix de conception et qui doit être réécrite, mais ce serait encore mieux si nous pouvions utiliser quelque chose sur étagère.

Voici un exemple du genre de chose que je recherche:

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

Après comparaison, l'utilitaire me dirait que "prop1" est différent entre les deux objets et "prop2" est le même. Je pense qu'il est plus naturel pour DiffDataStructure d'être un arbre, mais je ne vais pas être pointilleux si le code est fiable.

72
Kaypro II

Peut-être un peu en retard, mais j'étais dans la même situation que vous et j'ai fini par créer ma propre bibliothèque pour exactement votre cas d'utilisation. Comme j'ai été obligé de trouver une solution moi-même, j'ai décidé de la publier sur Github, pour épargner aux autres le dur labeur. Vous pouvez le trouver ici: https://github.com/SQiShER/Java-object-diff

--- Modifier ---

Voici un petit exemple d'utilisation basé sur le code 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;
36
SQiShER

http://javers.org est une bibliothèque qui fait exactement ce dont vous avez besoin: a des méthodes comme compare (Object leftGraph, Object rightGraph) renvoyant l'objet Diff. Diff contient une liste de changements (ReferenceChange, ValueChange, PropertyChange) par ex.

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

Il peut gérer les cycles dans les graphiques.

De plus, vous pouvez obtenir un instantané de n'importe quel objet graphique. Javers propose des sérialiseurs et des désérialiseurs JSON pour les instantanés et des modifications afin que vous puissiez facilement les enregistrer dans la base de données. Avec cette bibliothèque, vous pouvez facilement implémenter un module d'audit.

29
Paweł Szymczyk

Oui, la bibliothèque Java-util possède une classe GraphComparator qui comparera deux graphiques Java Object. Elle renvoie la différence sous forme de liste de deltas. Le GraphComparator vous permet également pour fusionner (appliquer) les deltas également. Ce code n'a que des dépendances sur le JDK, pas d'autres bibliothèques.

9
John DeRegnaucourt

Toute la bibliothèque Javers ne prend en charge que Java 7, j'étais dans une situation car je veux que cela soit utilisé pour un projet Java 6 donc je suis arrivé à prendre la source et changer d'une manière qui fonctionne pour Java 6 ci-dessous est le code github.

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

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

J'ai seulement changé le Java 7 pris en charge les conversions inhérentes '<>' en Java 6 support Je ne garantis pas que toutes les fonctionnalités fonctionneront puisque j'ai commenté quelques code inutile pour moi, il fonctionne pour toute comparaison d'objets personnalisés.

3
Sandesh

Peut-être que cela vous aidera, selon l'endroit où vous utilisez ce code, cela pourrait être utile ou problématique. Testé ce code.

    /**
 * @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

Vous pouvez également jeter un œil à la solution d'Apache. La plupart des projets l'ont déjà sur leur chemin de classe depuis sa partie de commons-lang.

Vérifier la différence pour un ou des domaines spécifiques:
http://commons.Apache.org/proper/commons-lang/javadocs/api-3.9/org/Apache/commons/lang3/builder/DiffBuilder.html

Vérifiez la différence en utilisant la réflexion:
http://commons.Apache.org/proper/commons-lang/javadocs/api-3.9/org/Apache/commons/lang3/builder/ReflectionDiffBuilder.html

0
John Blackwell