From 7066a54e17dcf2f09b8201b5739860961fc58f70 Mon Sep 17 00:00:00 2001 From: Thijs Broersen <4889512+ThijsBroersen@users.noreply.github.com> Date: Sun, 20 Oct 2024 19:11:59 +0200 Subject: [PATCH] feat: support ListMap (#1177) --- zio-json/shared/src/main/scala/zio/json/JsonCodec.scala | 4 ++++ zio-json/shared/src/main/scala/zio/json/JsonDecoder.scala | 7 +++++++ zio-json/shared/src/main/scala/zio/json/JsonEncoder.scala | 3 +++ zio-json/shared/src/test/scala/zio/json/CodecSpec.scala | 6 ++++++ zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala | 6 ++++++ zio-json/shared/src/test/scala/zio/json/EncoderSpec.scala | 3 +++ 6 files changed, 29 insertions(+) diff --git a/zio-json/shared/src/main/scala/zio/json/JsonCodec.scala b/zio-json/shared/src/main/scala/zio/json/JsonCodec.scala index 7034775b..32e3c53e 100644 --- a/zio-json/shared/src/main/scala/zio/json/JsonCodec.scala +++ b/zio-json/shared/src/main/scala/zio/json/JsonCodec.scala @@ -157,6 +157,10 @@ private[json] trait CodecLowPriority1 extends CodecLowPriority2 { this: JsonCode implicit def sortedSet[A: Ordering: JsonEncoder: JsonDecoder]: JsonCodec[immutable.SortedSet[A]] = JsonCodec(JsonEncoder.sortedSet[A], JsonDecoder.sortedSet[A]) + + implicit def listMap[K: JsonFieldEncoder: JsonFieldDecoder, V: JsonEncoder: JsonDecoder] + : JsonCodec[immutable.ListMap[K, V]] = + JsonCodec(JsonEncoder.listMap[K, V], JsonDecoder.listMap[K, V]) } private[json] trait CodecLowPriority2 extends CodecLowPriority3 { this: JsonCodec.type => diff --git a/zio-json/shared/src/main/scala/zio/json/JsonDecoder.scala b/zio-json/shared/src/main/scala/zio/json/JsonDecoder.scala index ddf45c72..5be8a54e 100644 --- a/zio-json/shared/src/main/scala/zio/json/JsonDecoder.scala +++ b/zio-json/shared/src/main/scala/zio/json/JsonDecoder.scala @@ -597,6 +597,13 @@ private[json] trait DecoderLowPriority1 extends DecoderLowPriority2 { def unsafeDecode(trace: List[JsonError], in: RetractReader): collection.SortedMap[K, V] = keyValueBuilder(trace, in, collection.SortedMap.newBuilder[K, V]) } + + implicit def listMap[K: JsonFieldDecoder, V: JsonDecoder]: JsonDecoder[immutable.ListMap[K, V]] = + new JsonDecoder[immutable.ListMap[K, V]] { + + def unsafeDecode(trace: List[JsonError], in: RetractReader): immutable.ListMap[K, V] = + keyValueBuilder(trace, in, immutable.ListMap.newBuilder[K, V]) + } } // We have a hierarchy of implicits for two reasons: diff --git a/zio-json/shared/src/main/scala/zio/json/JsonEncoder.scala b/zio-json/shared/src/main/scala/zio/json/JsonEncoder.scala index 6b1dc40e..7098a1b8 100644 --- a/zio-json/shared/src/main/scala/zio/json/JsonEncoder.scala +++ b/zio-json/shared/src/main/scala/zio/json/JsonEncoder.scala @@ -377,6 +377,9 @@ private[json] trait EncoderLowPriority1 extends EncoderLowPriority2 { implicit def sortedMap[K: JsonFieldEncoder, V: JsonEncoder]: JsonEncoder[collection.SortedMap[K, V]] = keyValueIterable[K, V, collection.SortedMap] + + implicit def listMap[K: JsonFieldEncoder, V: JsonEncoder]: JsonEncoder[immutable.ListMap[K, V]] = + keyValueIterable[K, V, immutable.ListMap] } private[json] trait EncoderLowPriority2 extends EncoderLowPriority3 { diff --git a/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala b/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala index 57f70b13..1cd755c7 100644 --- a/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala +++ b/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala @@ -182,6 +182,12 @@ object CodecSpec extends ZIOSpecDefault { assert(jsonStr.fromJson[Map[String, Int]])(isRight(equalTo(expected))) }, + test("ListMap") { + val jsonStr = """{"5XL":3,"2XL":14,"XL":159}""" + val expected = collection.immutable.ListMap("5XL" -> 3, "2XL" -> 14, "XL" -> 159) + + assert(jsonStr.fromJson[collection.immutable.ListMap[String, Int]])(isRight(equalTo(expected))) + }, test("zio.Chunk") { val jsonStr = """["5XL","2XL","XL"]""" val expected = Chunk("5XL", "2XL", "XL") diff --git a/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala b/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala index 73353af8..7f65c752 100644 --- a/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala +++ b/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala @@ -494,6 +494,12 @@ object DecoderSpec extends ZIOSpecDefault { assert(json.as[SortedMap[String, Int]])(isRight(equalTo(expected))) }, + test("ListMap") { + val json = Json.Obj("5XL" -> Json.Num(3), "2XL" -> Json.Num(14), "XL" -> Json.Num(159)) + val expected = immutable.ListMap("5XL" -> 3, "2XL" -> 14, "XL" -> 159) + + assert(json.as[immutable.ListMap[String, Int]])(isRight(equalTo(expected))) + }, test("Map, custom keys") { val json = Json.Obj("1" -> Json.Str("a"), "2" -> Json.Str("b")) val expected = Map(1 -> "a", 2 -> "b") diff --git a/zio-json/shared/src/test/scala/zio/json/EncoderSpec.scala b/zio-json/shared/src/test/scala/zio/json/EncoderSpec.scala index 59f484d4..914099e6 100644 --- a/zio-json/shared/src/test/scala/zio/json/EncoderSpec.scala +++ b/zio-json/shared/src/test/scala/zio/json/EncoderSpec.scala @@ -256,6 +256,9 @@ object EncoderSpec extends ZIOSpecDefault { assert(Map("hello" -> "world").toJsonPretty)(equalTo("{\n \"hello\" : \"world\"\n}")) && assert(Map("hello" -> Some("world"), "goodbye" -> None).toJsonPretty)( equalTo("{\n \"hello\" : \"world\"\n}") + ) && + assert(immutable.ListMap("hello" -> "world", "goodbye" -> "world").toJson)( + equalTo("""{"hello":"world","goodbye":"world"}""") ) }, test("Map, custom keys") {