115 votes

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

Existe-t-il une bibliothèque utilitaire Java qui soit analogue au programme Unix diff, 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 qui puisse comparer récursivement les différences dans les variables d'instance). Je suis no Je cherche une implémentation Java d'un diff texte. Je suis également no Je cherche de l'aide pour savoir comment utiliser la réflexion pour faire cela.

L'application dont je m'occupe a une implémentation fragile de cette fonctionnalité qui a fait l'objet de mauvais choix de conception et qui doit être réécrite, mais ce serait encore mieux si nous pouvions utiliser quelque chose de standard.

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 que "prop2" est le même. Je pense qu'il est plus naturel pour DiffDataStructure d'être un arbre, mais je ne vais pas faire la fine bouche si le code est fiable.

53voto

Daniel Bechler Points 379

C'est peut-être un peu tard, mais j'étais dans la même situation que vous et j'ai fini par créer ma propre bibliothèque exactement pour 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 à d'autres le travail difficile. Vous pouvez la trouver ici : https://github.com/SQiShER/java-object-diff

--- Editar ---

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

50voto

http://javers.org est une bibliothèque qui fait exactement ce dont vous avez besoin : elle possède des méthodes comme compare(Object leftGraph, Object rightGraph) qui renvoie l'objet Diff. Le 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 traiter les cycles dans les graphes.

En outre, vous pouvez obtenir un instantané de n'importe quel objet du graphique. Javers dispose de sérialiseurs et de désérialiseurs JSON pour les instantanés et les modifications, ce qui vous permet de les enregistrer facilement dans une base de données. Avec cette bibliothèque, vous pouvez facilement implémenter un module d'audit.

2voto

r0ast3d Points 2443

Peut-être que cela vous aidera. Selon l'endroit où vous utilisez ce code, il peut être utile ou problématique. J'ai 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();
        }
}

1voto

Bill Points 1556

DiffUtils a bien fonctionné pour moi. La bibliothèque est assez simple à utiliser. Voici un exemple de code tiré du site...

[Bah, ça aide si je lis le texte entier de la question. Ce qui s'en rapproche le plus, c'est un projet maintenant abandonné qui travaille à l'implémentation d'une version java de rsync. Vous pouvez le trouver aquí . On peut peut-être étudier cela pour trouver une solution.

-3voto

r0ast3d Points 2443

Une approche plus simple pour vous dire rapidement si deux objets sont différents serait d'utiliser la bibliothèque apache commons

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X