In your parent diff method you should iterate through your collections and compare each sub entities and append all differences in your parent diff result object. source
for (int i = 0; i < first.getDetails().size(); i++) {
diffBuilder.append("details[" + i + "].name",
first.getDetails().get(i).getName(),
second.getDetails().get(i).getName());
}
You may need to have the Detail object implement Diffable as well if it is more complex
for (int i = 0; i < first.getDetails().size(); i++) {
diffBuilder.append("details[" + i + "]",
first.getDetails().get(i).diff(second.getDetails().get(i)));
}
Granted This all assumes that the items in the list remain a certain set, and we are merely comparing the items by index. If however the list changes via removals or additions the standard behavior of the ReflectionDiffBuilder can detect said changes to the List as a whole and you can interpret the results afterward.
original.diff(changed).getDiffs().stream()
.flatMap(
diff -> {
if (diff.getLeft() instanceof Collection<?>) {
return Stream.of(
"%s dropped %s"
.formatted(
diff.getFieldName(),
CollectionUtils.removeAll(
(Collection<?>) diff.getLeft(), (Collection<?>) diff.getRight())),
"%s added %s"
.formatted(
diff.getFieldName(),
CollectionUtils.removeAll(
(Collection<?>) diff.getRight(), (Collection<?>) diff.getLeft())));
} else {
return Stream.of(
"%s changed from %s to %s"
.formatted(
diff.getFieldName(), diff.getLeft().toString(), diff.getRight()));
}
})
.toList();
Or finally Example 2 here makes creative use of the limitation that we could learn from
@lombok.Value
public class DiffableCollectionHolder<T> implements Diffable<Collection<T>> {
Collection<T> collection;
public DiffResult<Collection<T>> diff(Collection<T> obj) {
return DiffBuilder.<Collection<T>>builder().setLeft(this.getCollection())
.setRight(obj).build()
.append("removals", Collections.emptyList(), CollectionUtils.removeAll(this.getCollection(), obj))
.append("additions", Collections.emptyList(), CollectionUtils.removeAll(obj, this.getCollection())).build();
}
}