¿Hay una biblioteca de Java que puede "diferenciar" dos objetos?
diff (6)
¿Existe una biblioteca de utilidades de Java que sea análoga al programa de diferencia de Unix, pero para Objetos? Estoy buscando algo que pueda comparar dos objetos del mismo tipo y generar una estructura de datos que represente las diferencias entre ellos (y que pueda comparar recursivamente las diferencias en las variables de instancia). No estoy buscando una implementación de Java de un texto dif. Tampoco estoy buscando ayuda sobre cómo usar la reflexión para hacer esto.
La aplicación que estoy manteniendo 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 de la plataforma.
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 que "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.
Puede que sea un poco tarde, pero estaba en la misma situación que tú y terminé creando mi propia biblioteca exactamente para tu caso de uso. Como me vi obligado a encontrar una solución, decidí lanzarla en Github, para evitarle a los demás el trabajo duro. Lo puedes encontrar aquí: https://github.com/SQiShER/java-object-diff
--- Editar ---
Aquí hay un pequeño ejemplo de uso basado en el 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;
Quizás esto ayude, dependiendo de dónde use este código, podría ser útil o problemático. Probado 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();
}
}
Sí, la biblioteca github.com/jdereg/java-util tiene una clase GraphComparator que comparará dos Gráficos de objetos Java. Devuelve la diferencia como una lista de deltas. El GraphComparator también le permite fusionar (aplicar) los deltas también. Este código solo tiene dependencias en el JDK, no otras bibliotecas.
Toda la biblioteca de Javers tiene soporte solo para Java 7, estaba en una situación ya que quiero que se use para un proyecto de Java 6, así que por casualidad tomé la fuente y cambié de una manera que funciona para Java 6 a continuación está el código github .
https://github.com/sand3sh/javers-forJava6
Enlace del jar: https://github.com/sand3sh/javers-forJava6/blob/master/build/javers-forjava6.jar
Solo he cambiado las conversiones de conversión inherentes de Java 7 <>> a 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.
Un enfoque más simple para decirle rápidamente si dos objetos serían diferentes sería utilizar la biblioteca de apache commons
BeanComparator lastNameComparator = new BeanComparator("lname");
logger.info(" Match "+bc.compare(firstInstance, secondInstance));
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), por ejemplo
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 puedes obtener una instantánea de cualquier objeto gráfico. Javers tiene serializadores y deserializadores JSON para 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.