How to compare two objects and get the changed fields
Asked Answered
D

4

5

In here im logging the changes that has been done to a particular Object record. So im comparing the old record and the updated record to log the updated fields as a String. Any idea how can I do this?

Dinger answered 22/3, 2019 at 6:58 Comment(3)
Compare field by field and log the changed ones?Snooperscope
Seems your are not happy with comparing each attributes. In this case JAVERS can be an option.Ballot
Is your old record stored in an object and you set the new record to it, or you use 2 record object to store this 2 records?Crackleware
D
6

Well i found a solution as below :

  private static List<String> getDifference(Object s1, Object s2) throws IllegalAccessException {
    List<String> values = new ArrayList<>();
    for (Field field : s1.getClass().getDeclaredFields()) {
        field.setAccessible(true);
        Object value1 = field.get(s1);
        Object value2 = field.get(s2);
        if (value1 != null && value2 != null) {
            if (!Objects.equals(value1, value2)) {
                values.add(String.valueOf(field.getName()+": "+value1+" -> "+value2));
            }
        }
    }
    return values;
}
Dinger answered 22/3, 2019 at 8:18 Comment(0)
E
5

You may use javers library for this.

  <groupId>org.javers</groupId>
  <artifactId>javers-core</artifactId>

POJO:

    public class Person {
    private Integer id;
    private String name;

    // standard getters/constructors
}

Usage:

    @Test
public void givenPersonObject_whenApplyModificationOnIt_thenShouldDetectChange() {
    // given
    Javers javers = JaversBuilder.javers().build();

    Person person = new Person(1, "Michael Program");
    Person personAfterModification = new Person(1, "Michael Java");

    // when
    Diff diff = javers.compare(person, personAfterModification);

    // then
    ValueChange change = diff.getChangesByType(ValueChange.class).get(0);

    assertThat(diff.getChanges()).hasSize(1);
    assertThat(change.getPropertyName()).isEqualTo("name");
    assertThat(change.getLeft()).isEqualTo("Michael Program");
    assertThat(change.getRight()).isEqualTo("Michael Java");
}

Plus other use cases are supported as well.

Ellsworthellwood answered 22/3, 2019 at 10:16 Comment(1)
It does not answer to question. javers only shows difference, but does not provide information about field.Masha
R
1

Kotlin generic function with reflection

It's not a perfect answer to this question, but maybe someone could use it.

data class Difference(val old: Any?, val new: Any?)

fun findDifferencesInObjects(
    old: Any,
    new: Any,
    propertyPath: String? = null
): MutableMap<String, Difference> {
    val differences = mutableMapOf<String, Difference>()
    if (old::class != new::class) return differences

    @Suppress("UNCHECKED_CAST")
    old::class.memberProperties.map { property -> property as KProperty1<Any, *>
        val newPropertyPath = propertyPath?.plus('.')?.plus(property.name) ?: property.name
        property.isAccessible = true
        val oldValue = property.get(old)
        val newValue = property.get(new)

        if (oldValue == null && newValue == null) return@map
        if (oldValue == null || newValue == null) {
            differences[newPropertyPath] = Difference(oldValue, newValue)
            return@map
        }

        if (oldValue::class.isData || newValue::class.isData) {
            differences.putAll(findDifferencesInObject(oldValue, newValue, newPropertyPath))
        } else if (!Objects.equals(oldValue, newValue)) {
            differences[newPropertyPath] = Difference(oldValue, newValue)
        }
    }
    return differences
}

Result

{
  "some.nested.and.changed.field": {
    "old": "this value is old",
    "new": "this value is new"
  },
  ...
}
Rod answered 9/9, 2022 at 15:1 Comment(0)
P
0
  1. maybe this method can help you to solve you problem
     /**
     * get o1 and o2 different value of field name
     * @param o1 source
     * @param o2 target
     * @return
     * @throws IllegalAccessException
     */
    public static List<String> getDiffName(Object o1,Object o2) throws IllegalAccessException {
        //require o1 and o2 is not null
        if (o1==null&&o2==null){
            return Collections.emptyList();
        }
        //if only one has null
        if (o1 == null){
            return getAllFiledName(o2);
        }
        if (o2 == null){
            return getAllFiledName(o1);
        }
        //source field
        Field[] fields=o1.getClass().getDeclaredFields();
        List<String> fieldList=new ArrayList<>(fields.length);
        //if class is same using this to call
        if (o1.getClass().equals(o2.getClass())){
            //loop field to equals the field
            for (Field field : fields) {
                //to set the field access
                field.setAccessible(true);
                Object source = field.get(o1);
                Object target = field.get(o2);
                //using jdk8 equals to compare two objects
                if (!Objects.equals(source, target)){
                    fieldList.add(field.getName());
                }
            }
        }else {
            //maybe o1 class is not same as o2 class
            Field[] targetFields=o2.getClass().getDeclaredFields();
            List<String> sameFieldNameList=new ArrayList<>();
            //loop o1 field
            for (Field field : fields) {
                String name = field.getName();
                //loop target field to get same field
                for (Field targetField : targetFields) {
                    //if name is equal to compare
                    if (targetField.getName().equals(name)){
                        //add same field to list
                        sameFieldNameList.add(name);
                        //set access
                        field.setAccessible(true);
                        Object source = field.get(o1);
                        //set target access
                        targetField.setAccessible(true);
                        Object target = targetField.get(o2);
                        //equals
                        if (!Objects.equals(source, target)){
                            fieldList.add(field.getName());
                        }
                    }
                }
            }
            //after loop add different source
            for (Field targetField : targetFields) {
                //add not same field
                if (!sameFieldNameList.contains(targetField.getName())){
                    fieldList.add(targetField.getName());
                }
            }
        }
        return fieldList;
    }

    /**
     * getAllFiledName
     * @param obj
     * @return
     */
    private static List<String> getAllFiledName(Object obj) {
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        List<String> list=new ArrayList<>(declaredFields.length);
        for (Field field : declaredFields) {
            list.add(field.getName());
        }
        return list;
    }
  1. this method can compare two object which have the same name field ,if they don't have same field,return all field Name
Pyrimidine answered 22/3, 2019 at 7:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.