I have 2 classes:
@Data @Builder public class Boss { private String name; private List<Employee> subordinates; } @Data @Builder public class Employee { private String name; private Boolean hasDrivingLicense; private List<Employee> colleagues; }
And also my own defined comparators(rules):
public class StringComparator implements CustomValueComparator<String> { public boolean equals(String s, String t1) { return (s != null && s.equals("") && t1 == null) || (s == null && t1 != null && t1.equals("")); } public String toString(String s) { return s; } } public class CollectionComparator<T> implements CustomValueComparator<Collection<T>> { public boolean equals(Collection<T> ts, Collection<T> t1) { return (ts == null && t1 != null && t1.isEmpty()) || (ts != null && ts.isEmpty() && t1 == null); } public String toString(Collection<T> ts) { return ts.toString(); } } public class BooleanComparator implements CustomValueComparator<Boolean> { public boolean equals(Boolean aBoolean, Boolean t1) { return (aBoolean == null && t1 != null && t1.equals(false)) || (aBoolean != null && aBoolean.equals(false) && t1 == null); } public String toString(Boolean aBoolean) { return aBoolean.toString(); } }
These comparators are defined so that an empty String is treated the same as null, an empty Collection is treated the same as null, and a Boolean false is treated the same as null. In the main method I try to compare 2 objects so as to check if the defined comparators work.
public static void main(String[] args) { Employee employee1 = Employee.builder() .name("Krzysztof") .colleagues(new ArrayList<>()) .hasDrivingLicense(false) .build(); Employee employee2 = Employee.builder() .name("Krzysztof") .colleagues(null) .hasDrivingLicense(null) .build(); Boss boss1 = Boss.builder() .name("") .subordinates(Arrays.asList(employee1)) .build(); Boss boss2 = Boss.builder() .name(null) .subordinates(Arrays.asList(employee2)) .build(); final Javers javers = JaversBuilder.javers() .registerValue(CollectionComparator.class) .registerValue(StringComparator.class) .registerValue(BooleanComparator.class) .build(); final Diff diff = javers.compare(boss1, boss2); System.out.println(diff.getChanges().size() == 0); System.out.println(diff.getChanges().size()); System.out.println(diff); }
Unfortunately, the comparison does not work as expected. In JaversBuilder I tried add .registerValue(String.class, (a, b) -> StringUtils.equals(a,b)) but it also dont work. Result of print:
false 2 Diff: * changes on Boss/ : - 'name' value changed from '' to '' - 'subordinates/0.hasDrivingLicense' value changed from 'false' to ''
What should I change to make it work. If comparators were working correctly In this example, diff would not have any changes
Advertisement
Answer
I think this problem is caused by your assumption that CustomValueComparators
can provide some custom logic for nulls. This assumption is sensible but false. Currently, CustomValueComparators
always get non-null values to compare. Javers itself deals with nuls.
I have created an issue to manage this https://github.com/javers/javers/issues/1075