Existe uma biblioteca de utilitários Java que é análoga ao programa diff do Unix, mas para objetos? Estou procurando por algo que pode comparar dois objetos do mesmo tipo e gerar uma estrutura de dados que representa as diferenças entre eles (e pode comparar recursivamente diferenças em variáveis de instância). Estou não à procura de uma implementação Java de um diff texto. Também não estou procurando ajuda sobre como usar a reflexão para fazer isso.
O aplicativo que estou mantendo tem uma implementação frágil dessa funcionalidade que teve algumas escolhas de design ruins e que precisa ser reescrita, mas seria ainda melhor se pudéssemos usar algo disponível.
Aqui está um exemplo do tipo de coisa que estou procurando:
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
Após a comparação, o utilitário me diria que "prop1" é diferente entre os dois objetos e "prop2" é o mesmo. Eu acho que é mais natural para DiffDataStructure ser uma árvore, mas não vou ser exigente se o código for confiável.
Respostas:
Pode ser um pouco tarde, mas eu estava na mesma situação que você e acabei criando minha própria biblioteca exatamente para o seu caso de uso. Como fui forçado a encontrar uma solução sozinho, decidi lançá-lo no Github, para poupar os outros do trabalho duro. Você pode encontrá-lo aqui: https://github.com/SQiShER/java-object-diff
--- Editar ---
Aqui está um pequeno exemplo de uso com base no código 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;
fonte
http://javers.org é uma biblioteca que faz exatamente o que você precisa: tem métodos como compare (Object leftGraph, Object rightGraph) retornando o objeto Diff. Diff contém uma lista de mudanças (ReferenceChange, ValueChange, PropertyChange), por exemplo
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
Ele pode lidar com ciclos em gráficos.
Além disso, você pode obter um instantâneo de qualquer objeto gráfico. Javers tem serializadores e desserializadores JSON para instantâneos e alterações para que você possa salvá-los facilmente no banco de dados. Com esta biblioteca, você pode implementar facilmente um módulo de auditoria.
fonte
Sim, a biblioteca java-util tem uma classe GraphComparator que irá comparar dois Java Object Graphs. Ele retorna a diferença como uma Lista de deltas. O GraphComparator também permite mesclar (aplicar) os deltas. Este código só tem dependências no JDK, nenhuma outra biblioteca.
fonte
Toda a biblioteca Javers tem suporte apenas para Java 7, eu estava em uma situação em que quero que isso seja usado para um projeto Java 6, então peguei o código-fonte e mudei de forma que funciona para Java 6 abaixo é o código do github .
https://github.com/sand3sh/javers-forJava6
Link do jar: https://github.com/sand3sh/javers-forJava6/blob/master/build/javers-forjava6.jar
Eu só mudei as conversões de cast inerentes '<>' suportadas por Java 7 para suporte a Java 6. Não garanto que todas as funcionalidades funcionarão, pois comentei alguns códigos desnecessários para mim que funcionam para todas as comparações de objetos personalizados.
fonte
Você também pode dar uma olhada na solução do Apache. A maioria dos projetos já o tem em seu classpath desde sua parte do commons-lang.
Verifique a diferença para campo (s) específico (s):
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/DiffBuilder.html
Verifique a diferença usando reflexão:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/ReflectionDiffBuilder.html
fonte
Talvez isso ajude, dependendo de onde você usa esse código, pode ser útil ou problemático. Testei este código.
/** * @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(); } }
fonte
Fields
não verificaria métodos. Os métodos podem ser qualquer coisagetRandomNumber()
.Uma abordagem mais simples para dizer rapidamente se dois objetos são diferentes é usar a biblioteca apache commons
BeanComparator lastNameComparator = new BeanComparator("lname"); logger.info(" Match "+bc.compare(firstInstance, secondInstance));
fonte