From e4c8b61f8d0c66444a4db8fc7e86c7f378c8adcc Mon Sep 17 00:00:00 2001 From: Andriy Plokhotnyuk Date: Sat, 17 Aug 2019 10:40:18 +0200 Subject: [PATCH] Yet more safe and efficient removing of keys without an internal `toSet` call that isn't safe: https://github.com/scala/bug/issues/11203 --- .../com/github/pathikrit/dijon/package.scala | 19 ++++++++++++------- .../github/pathikrit/dijon/DijonSpec.scala | 11 ++++++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/main/scala/com/github/pathikrit/dijon/package.scala b/src/main/scala/com/github/pathikrit/dijon/package.scala index 3bf0dd5..c554c20 100644 --- a/src/main/scala/com/github/pathikrit/dijon/package.scala +++ b/src/main/scala/com/github/pathikrit/dijon/package.scala @@ -57,22 +57,27 @@ package object dijon { def ++(that: SomeJson): SomeJson = (this.underlying, that.underlying) match { case (a: JsonObject, b: JsonObject) => - val res = a.clone() - b.keys foreach { - case key if res contains key => res(key) = res(key) ++ b(key) - case key => res(key) = b(key) + val res = new util.LinkedHashMap[String, SomeJson](a.size + b.size) + a.foreach { case (k, v) => + res.put(k, if (b.contains(k)) v ++ b(k) else v.deepCopy) } - res + b.foreach { case (k, v) => + if (!res.containsKey(k)) res.put(k, v.deepCopy) + } + res.asScala case _ => that.deepCopy } def --(keys: String*): SomeJson = underlying match { - case obj: JsonObject => obj -- keys + case obj: JsonObject => + val res = obj.clone() + keys.foreach(res -= _) + res.deepCopy case _ => deepCopy } def remove(keys: String*): Unit = underlying match { - case obj: JsonObject => obj --= keys + case obj: JsonObject => keys.foreach(obj -= _) case _ => () } diff --git a/src/test/scala/com/github/pathikrit/dijon/DijonSpec.scala b/src/test/scala/com/github/pathikrit/dijon/DijonSpec.scala index da5bab6..01ad804 100644 --- a/src/test/scala/com/github/pathikrit/dijon/DijonSpec.scala +++ b/src/test/scala/com/github/pathikrit/dijon/DijonSpec.scala @@ -248,7 +248,7 @@ class DijonSpec extends FunSuite { assert(t.b.c.a == None) } - test("handle merges") { + test("handle deep merges") { val scala = json""" { "name": "scala", @@ -281,6 +281,15 @@ class DijonSpec extends FunSuite { assert(scala == scalaCopy) // original json objects stay untouched after merging assert(java == javaCopy) + + val ab = json"""{"a":{"b":[0,1]}}""" + val ac = json"""{"a":{"c":[1,2]}}""" + val `ab++ac` = ab ++ ac // merge result should be not affected by subsequent mutation of arguments + ab.a.b(0) = json"""5""" + ac.a.c(0) = json"""3""" + assert(ab == json"""{"a":{"b":[5,1]}}""") + assert(ac == json"""{"a":{"c":[3,2]}}""") + assert(`ab++ac` == json"""{"a":{"b":[0,1],"c":[1,2]}}""") } test("be type-safeish") {