Czy istnieje biblioteka narzędzi Java, która jest analogiczna do programu różnicowego w systemie Unix, ale dla obiektów? Szukam czegoś, co może porównać dwa obiekty tego samego typu i wygenerować strukturę danych, która reprezentuje różnice między nimi (i może rekurencyjnie porównywać różnice w zmiennych instancji). Ja nie szukam implementacji Java diff tekstowym. Ja również nie szuka pomocy, jak korzystać z refleksji, aby to zrobić.
Aplikacja, którą utrzymuję, ma delikatną implementację tej funkcji, która miała słabe wybory projektowe i którą trzeba przepisać, ale byłoby jeszcze lepiej, gdybyśmy mogli użyć czegoś z półki.
Oto przykład tego, czego szukam:
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
Po porównaniu narzędzie powiedziałoby mi, że „prop1” różni się między dwoma obiektami, a „prop2” jest tym samym. Myślę, że to najbardziej naturalne, że DiffDataStructure jest drzewem, ale nie będę wybredny, jeśli kod jest niezawodny.
Odpowiedzi:
Może się trochę spóźnić, ale byłem w takiej samej sytuacji jak ty i ostatecznie stworzyłem własną bibliotekę dla dokładnie twojego przypadku użycia. Ponieważ musiałem sam wymyślić rozwiązanie, zdecydowałem się opublikować je na Githubie, aby oszczędzić innym ciężkiej pracy. Możesz go znaleźć tutaj: https://github.com/SQiShER/java-object-diff
--- Edytuj ---
Oto mały przykład użycia oparty na kodzie 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;
źródło
http://javers.org to biblioteka, która robi dokładnie to, czego potrzebujesz: zawiera metody takie jak compare (Object leftGraph, Object rightGraph) zwracające obiekt Diff. Diff zawiera listę zmian (ReferenceChange, ValueChange, PropertyChange) np
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
Może obsługiwać cykle na wykresach.
Dodatkowo możesz uzyskać migawkę dowolnego obiektu wykresu. Javers ma serializatory JSON i deserializatory do migawki i zmiany, dzięki czemu można je łatwo zapisać w bazie danych. Dzięki tej bibliotece możesz łatwo wdrożyć moduł do audytu.
źródło
Tak, biblioteka java-util zawiera klasę GraphComparator, która porównuje dwa wykresy obiektów Java. Zwraca różnicę w postaci listy delt. GraphComparator pozwala również na scalanie (stosowanie) delt. Ten kod ma zależności tylko od JDK, żadnych innych bibliotek.
źródło
Cała biblioteka Javers obsługuje tylko Java 7, byłem w sytuacji, ponieważ chcę, aby była używana w projekcie Java 6, więc zdarzyło mi się wziąć źródło i zmienić sposób, w jaki działa dla Java 6 poniżej jest kod github .
https://github.com/sand3sh/javers-forJava6
Link do słoika: https://github.com/sand3sh/javers-forJava6/blob/master/build/javers-forjava6.jar
Zmieniłem tylko nieodłączne konwersje rzutowania obsługiwane przez Javę 7 na obsługę Java 6 Nie gwarantuję, że wszystkie funkcje będą działać, ponieważ skomentowałem kilka niepotrzebnych dla mnie kodów, które działają dla wszystkich porównań obiektów niestandardowych.
źródło
Możesz również rzucić okiem na rozwiązanie z Apache. Większość projektów ma go już na swojej ścieżce klas, ponieważ jest częścią wspólnego języka.
Sprawdź różnice dla określonych pól:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/DiffBuilder.html
Sprawdź różnicę za pomocą refleksji:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/ReflectionDiffBuilder.html
źródło
Może to pomoże, w zależności od tego, gdzie używasz tego kodu, może być przydatne lub problematyczne. Przetestowałem ten kod.
/** * @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(); } }
źródło
Fields
nie metody. Metody mogą być dowolnegetRandomNumber()
.Prostszym podejściem do szybkiego stwierdzenia, czy dwa obiekty są różne, byłoby skorzystanie z biblioteki Apache commons
BeanComparator lastNameComparator = new BeanComparator("lname"); logger.info(" Match "+bc.compare(firstInstance, secondInstance));
źródło