Esiste una libreria Java in grado di “diff” due oggetti?

Esiste una libreria di utilità Java analoga al programma Unix diff, ma per gli oggetti? Sto cercando qualcosa che possa confrontare due oggetti dello stesso tipo e generare una struttura dati che rappresenti le differenze tra loro (e può ricorsivamente confrontare le differenze nelle variabili di istanza). Non sto cercando un’implementazione Java di un testo diff. Non sto nemmeno cercando aiuto su come usare la riflessione per farlo.

L’applicazione che sto mantenendo ha una fragile implementazione di questa funzionalità che ha avuto alcune scelte di progettazione scadenti e che deve essere riscritta, ma sarebbe ancora meglio se potessimo usare qualcosa da uno scaffale.

Ecco un esempio del tipo di cosa che sto cercando:

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 

Dopo il confronto, l’utilità mi diceva che “prop1” è diverso tra i due oggetti e “prop2” è lo stesso. Penso che sia naturale per DiffDataStructure essere un albero, ma non sarò pignolo se il codice è affidabile.

Potrebbe essere un po ‘in ritardo, ma mi trovavo nella stessa situazione come te e ho finito per creare la mia libreria per il tuo caso d’uso. Dato che sono stato costretto a trovare una soluzione da solo, ho deciso di rilasciarlo su Github, per risparmiare agli altri il duro lavoro. Puoi trovarlo qui: https://github.com/SQiShER/java-object-diff

Modifica

Ecco un piccolo esempio di utilizzo basato sul codice 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; 

http://javers.org è una libreria che fa esattamente ciò di cui hai bisogno: ha metodi come compare (Object leftGraph, Object rightGraph) che restituisce l’object Diff. Diff contiene un elenco di modifiche (ReferenceChange, ValueChange, PropertyChange) ad es

 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 

Può gestire cicli nei grafici.

Inoltre è ansible ottenere l’istantanea di qualsiasi object grafico. Javers ha serializzatori e deserializzatori JSON per lo snapshot e modifiche in modo da poterli facilmente salvare nel database. Con questa libreria puoi facilmente implementare un modulo per il controllo.

Sì, la libreria java-util ha una class GraphComparator che confronterà due grafici oggetti Java. Restituisce la differenza come una lista di delta. GraphComparator ti consente anche di unire (applicare) i delta. Questo codice ha solo dipendenze su JDK, nessun’altra libreria.

Tutta la libreria Javers ha il supporto solo per Java 7, ero in una situazione dal momento che voglio che questo sia usato per un progetto Java 6 quindi mi è capitato di prendere la fonte e cambiare in un modo in cui funziona per Java 6 sotto c’è il codice github .

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

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

Ho solo modificato le conversioni di cast intrinseche supportate da Java 7 al supporto di Java 6 Non garantisco tutte le funzionalità funzioneranno poiché ho commentato qualche codice non necessario per me funziona per tutti gli oggetti personalizzati di confronto.

Forse questo ti aiuterà, a seconda di dove usi questo codice, potrebbe essere utile o problematico. Testato questo codice.

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

Un approccio più semplice per dirti rapidamente se due oggetti sono diversi è usare la libreria di apache commons

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