¿Hay una biblioteca de Java que pueda “dif” dos Objetos?

¿Existe una biblioteca de utilidades Java que sea análoga a la diferencia de progtwig 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 pueda comparar recursivamente las diferencias en las variables de instancia). No estoy buscando una implementación Java de una diferencia de texto. Tampoco busco 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 dice 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 quisquilloso si el código es confiable.

Podría ser un poco tarde, pero yo estaba en la misma situación que usted y terminé creando mi propia biblioteca para su caso de uso. Como me vi obligado a buscar una solución yo mismo, decidí lanzarla en Github, para evitarles a otros 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 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 es una biblioteca que hace exactamente lo que necesita: tiene métodos como comparar (Object leftGraph, Object rightGraph) devolver el objeto Diff. Diff contiene una lista de cambios (ReferenceChange, ValueChange, PropertyChange), por 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 en 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 para auditar.

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 también. Este código solo tiene dependencias en el JDK, no hay otras bibliotecas.

Toda la biblioteca de Javers tiene soporte solo para Java 7, yo estaba en una situación ya que quiero que esto se use para un proyecto de Java 6, así que tomé la fuente y el cambio de una manera que funciona para Java 6 a continuación está el código github .

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

Enlace 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 Java 6. No garantizo que todas las funcionalidades funcionen, ya que he comentado algunos códigos innecesarios para mí. Funciona para todas las comparaciones de objetos personalizados.

Tal vez 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(); } } 

Un enfoque más simple para decirle rápidamente si dos objetos son diferentes sería usar la biblioteca de apache commons

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