From b9f027611302298cd1a8eb91296d51066a72f566 Mon Sep 17 00:00:00 2001 From: Doug Roper Date: Mon, 22 Nov 2021 05:09:22 -0500 Subject: [PATCH] Better handle String hashCode collisions in JsLookup --- .../json/JsonParsing_01_ParseManyFields.scala | 25 ++++++++++++++++++- .../scala/play/api/libs/json/JsLookup.scala | 6 ++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/benchmarks/src/main/scala/play/api/libs/json/JsonParsing_01_ParseManyFields.scala b/benchmarks/src/main/scala/play/api/libs/json/JsonParsing_01_ParseManyFields.scala index 0b9db67f9..049ea3d76 100644 --- a/benchmarks/src/main/scala/play/api/libs/json/JsonParsing_01_ParseManyFields.scala +++ b/benchmarks/src/main/scala/play/api/libs/json/JsonParsing_01_ParseManyFields.scala @@ -6,6 +6,9 @@ package play.api.libs.json import org.openjdk.jmh.annotations._ +/** + * @see https://github.com/playframework/play-json/pull/193 + */ @State(Scope.Benchmark) class JsonParsing_01_ParseManyFields { @Param(Array("10", "100", "1000", "10000", "100000")) @@ -13,6 +16,11 @@ class JsonParsing_01_ParseManyFields { var stringToParse: String = _ + case class Example(s: String) + object Example { + implicit val reads = Json.reads[Example] + } + @Setup def setup(): Unit = { val value = "42" @@ -21,7 +29,22 @@ class JsonParsing_01_ParseManyFields { } @Benchmark - def parseObject(): Unit = { + def parseObject(): JsValue = { Json.parse(stringToParse) } + + @Benchmark + def parseObjectAs(): Example = { + Json.parse(stringToParse).as[Example] + } + + @Benchmark + def parseObjectLookup(): JsValue = { + (Json.parse(stringToParse) \ "s").as[JsString] + } + + @Benchmark + def parseObjectValue(): Option[JsValue] = { + Json.parse(stringToParse).as[JsObject].value.get("s") + } } diff --git a/play-json/shared/src/main/scala/play/api/libs/json/JsLookup.scala b/play-json/shared/src/main/scala/play/api/libs/json/JsLookup.scala index 1a5c313a9..7e82b2cfa 100644 --- a/play-json/shared/src/main/scala/play/api/libs/json/JsLookup.scala +++ b/play-json/shared/src/main/scala/play/api/libs/json/JsLookup.scala @@ -68,7 +68,7 @@ case class JsLookup(result: JsLookupResult) extends AnyVal { case JsDefined(x) => x match { case arr: JsObject => - arr.value.get(fieldName) match { + arr.underlying.get(fieldName) match { case Some(x) => x case None => throw new NoSuchElementException(String.valueOf(fieldName)) } @@ -101,7 +101,7 @@ case class JsLookup(result: JsLookupResult) extends AnyVal { */ def \(fieldName: String): JsLookupResult = result match { case JsDefined(obj @ JsObject(_)) => - obj.value + obj.underlying .get(fieldName) .map(JsDefined.apply) .getOrElse(JsUndefined(s"'$fieldName' is undefined on object: $obj")) @@ -117,7 +117,7 @@ case class JsLookup(result: JsLookupResult) extends AnyVal { */ def \\(fieldName: String): collection.Seq[JsValue] = result match { case JsDefined(obj: JsObject) => - obj.value.foldLeft(Seq[JsValue]())((o, pair) => + obj.underlying.foldLeft(Seq[JsValue]())((o, pair) => pair match { case (key, value) if key == fieldName => o ++ (value +: (value \\ fieldName)) case (_, value) => o ++ (value \\ fieldName)