java-object-diff icon indicating copy to clipboard operation
java-object-diff copied to clipboard

Differences in excluded fields in objects of a collection cause state of collection diff to be changed

Open luispollo opened this issue 4 years ago • 3 comments

Hi Daniel,

First of all, thanks a lot for the awesome library! We've been using it in open-source Spinnaker for over a year now with great results.

I think I may have run into a corner case. We are configuring a differ in Kotlin like so:

    val differ: ObjectDiffer = ObjectDifferBuilder
      .startBuilding()
      .apply {
        comparison().ofType(Instant::class.java).toUseEqualsMethod()
        inclusion().resolveUsing(object : InclusionResolver {
          override fun getInclusion(node: DiffNode): Inclusion =
            if (node.getPropertyAnnotation<ExcludedFromDiff>() != null) EXCLUDED else INCLUDED

          override fun enablesStrictIncludeMode() = false
        })
      }
      .build()

This allows us to use a @ExcludedFromDiff annotation to exclude unwanted properties from the diff. This normally works just fine, but I came across a case where, if the objects with excluded fields are part of a collection, and I compare the collection itself, the differ reports a state of CHANGED instead of the expected UNTOUCHED.

For example, this comparison reports UNTOUCHED:

data class MyObject(
  @get:ExcludedFromDiff
  val prop: String
)

val base = MyObject("test")
differ.compare(base, base.copy(prop = "whatever"))

But this comparison reports CHANGED:

differ.compare(setOf(base), setOf(base.copy(prop = "whatever")))

I've tried my best to debug the issue, but failed. If you have any insights and could at least confirm whether you think this might be a bug and whether there's any workaround in version 0.95, that would be greatly appreciated!

luispollo avatar Jan 25 '21 01:01 luispollo

Cc: @robfletcher, @lorin

luispollo avatar Jan 25 '21 01:01 luispollo

@lorin just pointed out that this may be a duplicate of https://github.com/SQiShER/java-object-diff/issues/96

luispollo avatar Jan 25 '21 01:01 luispollo

比较两个集合对象, 会执行de.danielbechler.diff.differ.CollectionDiffer比较器逻辑:

  • 遍历working集合中的每个元素, 如果不存在于base集合中, 则认为working集合新增了元素
  • 遍历base集合中的每个元素, 如果不存在于working集合中, 则认为working集合删除了元素
  • 元素同时存在于两个集合中, 才会继续比较每个属性的值, 这种情况你的配置才会生效

关键代码:

de.danielbechler.diff.differ.CollectionDiffer#compareInternally de.danielbechler.diff.access.CollectionItemAccessor#get de.danielbechler.diff.identity.EqualsIdentityStrategy#equals

判断集合中元素是否相同, 默认调用对象的equals方法, 基于此, 提供两种解决办法:

  1. 可以重写数据类的equalshashCode方法, 忽略添加ExcludedFromDiff注解的字段
  2. 自定义并配置IdentityStrategy

syzsh avatar Jun 15 '22 14:06 syzsh