Есть ли служебная библиотека Java, аналогичная программе diff для Unix, но для объектов? Я ищу что-то, что может сравнивать два объекта одного типа и генерировать структуру данных, которая представляет различия между ними (и может рекурсивно сравнивать различия в переменных экземпляра). Я не ищу Java-реализацию текстового diff. Я также не ищу помощи в том, как использовать для этого отражение.
Приложение, которое я поддерживаю, имеет хрупкую реализацию этой функциональности, в которой были некоторые неудачные варианты дизайна и которую необходимо переписать, но было бы еще лучше, если бы мы могли использовать что-то готовое.
Вот пример того, что я ищу:
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
После сравнения утилита сообщила бы мне, что «prop1» у этих двух объектов различается, а «prop2» - то же самое. Я думаю, что для DiffDataStructure наиболее естественно быть деревом, но я не буду привередничать, если код надежен.
Ответы:
Может быть, немного поздно, но я оказался в той же ситуации, что и вы, и в итоге создал свою собственную библиотеку именно для вашего варианта использования. Поскольку я был вынужден сам придумать решение, я решил выпустить его на Github, чтобы избавить других от тяжелой работы. Вы можете найти его здесь: https://github.com/SQiShER/java-object-diff
--- Редактировать ---
Вот небольшой пример использования на основе кода 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;
источник
http://javers.org - это библиотека, которая делает именно то, что вам нужно: имеет такие методы, как compare (Object leftGraph, Object rightGraph), возвращающие объект Diff. Diff содержит список изменений (ReferenceChange, ValueChange, PropertyChange), например
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
Он может обрабатывать циклы в графах.
Кроме того, вы можете получить снимок любого графического объекта. У Javers есть сериализаторы и десериализаторы JSON для моментальных снимков и изменений, поэтому вы можете легко сохранить их в базе данных. С помощью этой библиотеки вы можете легко реализовать модуль для аудита.
источник
Да, в библиотеке java-util есть класс GraphComparator, который будет сравнивать два графа объектов Java. Он возвращает разницу в виде списка дельт. GraphComparator также позволяет вам объединять (применять) дельты. Этот код зависит только от JDK, а не от других библиотек.
источник
Вся библиотека Javers поддерживает только Java 7, я был в ситуации, так как хочу, чтобы это использовалось для проекта Java 6, поэтому я случайно взял исходный код и изменил способ, которым он работает для Java 6, ниже - это код github .
https://github.com/sand3sh/javers-forJava6
Ссылка на Jar: https://github.com/sand3sh/javers-forJava6/blob/master/build/javers-forjava6.jar
Я только изменил встроенные преобразования приведения типов '<>', поддерживаемые Java 7, на поддержку Java 6. Я не гарантирую, что все функции будут работать, так как я прокомментировал несколько ненужных кодов для меня, он работает для сравнения всех настраиваемых объектов.
источник
Вы также можете взглянуть на решение от Apache. У большинства проектов он уже есть в пути к классам, так как он является частью commons-lang.
Проверьте разницу для конкретных полей:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/DiffBuilder.html
Проверьте разницу с помощью отражения:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/ReflectionDiffBuilder.html
источник
Возможно, это поможет, в зависимости от того, где вы используете этот код, это может быть полезно или проблематично. Протестировал этот код.
/** * @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(); } }
источник
Fields
не методы. Методы могут быть любыми, вродеgetRandomNumber()
.Более простой подход, чтобы быстро определить, отличаются ли два объекта, - это использовать библиотеку общего пользования apache.
BeanComparator lastNameComparator = new BeanComparator("lname"); logger.info(" Match "+bc.compare(firstInstance, secondInstance));
источник