¿Existe una biblioteca de utilidades de Java que sea análoga al programa diff de Unix, pero para Objects? Estoy buscando algo que pueda comparar dos objetos del mismo tipo y generar una estructura de datos que represente las diferencias entre ellos (y pueda comparar de forma recursiva diferencias en variables de instancia). Estoy no en busca de una implementación en Java de un diff texto. También estoy no en busca de ayuda con la forma de utilizar la reflexión para hacer esto.
La aplicación que mantengo tiene una implementación frágil de esta funcionalidad que tenía algunas opciones de diseño deficientes y que necesita ser reescrita, pero sería aún mejor si pudiéramos usar algo disponible.
Aquí hay un ejemplo del tipo de cosas que estoy buscando:
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
Después de la comparación, la utilidad me diría que "prop1" es diferente entre los dos objetos y "prop2" es el mismo. Creo que es más natural que DiffDataStructure sea un árbol, pero no voy a ser exigente si el código es confiable.
Respuestas:
Puede que sea un poco tarde, pero yo estaba en la misma situación que tú y terminé creando mi propia biblioteca para exactamente tu caso de uso. Como me vi obligado a encontrar una solución, decidí publicarla en Github, para ahorrarles a los demás el trabajo duro. Puede encontrarlo aquí: https://github.com/SQiShER/java-object-diff
--- Editar ---
Aquí hay un pequeño ejemplo de uso basado en el código de 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;
fuente
http://javers.org es una biblioteca que hace exactamente lo que necesita: tiene métodos como comparar (Object leftGraph, Object rightGraph) que devuelve el objeto Diff. Diff contiene una lista de cambios (ReferenceChange, ValueChange, PropertyChange) p. Ej.
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
Puede manejar ciclos en gráficos.
Además, puede obtener una instantánea de cualquier objeto gráfico. Javers tiene serializadores y deserializadores JSON para realizar instantáneas y cambios para que pueda guardarlos fácilmente en la base de datos. Con esta biblioteca, puede implementar fácilmente un módulo de auditoría.
fuente
Sí, la biblioteca java-util tiene una clase GraphComparator que comparará dos gráficos de objetos Java. Devuelve la diferencia como una lista de deltas. GraphComparator también le permite fusionar (aplicar) los deltas. Este código solo tiene dependencias del JDK, no de otras bibliotecas.
fuente
Toda la biblioteca de Javers tiene soporte solo para Java 7, estaba en una situación ya que quería que esto se usara para un proyecto de Java 6, así que tomé la fuente y cambié de una manera que funciona para Java 6 a continuación es el código github .
https://github.com/sand3sh/javers-forJava6
Enlace de jar: https://github.com/sand3sh/javers-forJava6/blob/master/build/javers-forjava6.jar
Solo he cambiado las conversiones de conversión inherentes '<>' compatibles con Java 7 a la compatibilidad con Java 6.No garantizo que todas las funcionalidades funcionarán, ya que he comentado algunos códigos innecesarios para mí, funciona para todas las comparaciones de objetos personalizados.
fuente
También puede echar un vistazo a la solución de Apache. La mayoría de los proyectos ya lo tienen en su classpath ya que es parte de commons-lang.
Verifique la diferencia para campos específicos:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/DiffBuilder.html
Verifique la diferencia usando la reflexión:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/ReflectionDiffBuilder.html
fuente
Quizás esto ayude, dependiendo de dónde use este código, podría ser útil o problemático. Probé 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(); } }
fuente
Fields
métodos no. Los métodos pueden ser cualquier cosa, comogetRandomNumber()
.Un enfoque más simple para decirle rápidamente si dos objetos son diferentes sería usar la biblioteca apache commons
BeanComparator lastNameComparator = new BeanComparator("lname"); logger.info(" Match "+bc.compare(firstInstance, secondInstance));
fuente