From 7cc1d1efd4343b08cdaae1553a3813a54b58743e Mon Sep 17 00:00:00 2001 From: Thijs Broersen Date: Fri, 28 Jun 2024 21:58:23 +0200 Subject: [PATCH] feat: string-like field-decoder --- .../src/main/scala/zio/json/JsonFieldDecoder.scala | 12 +++++++++++- .../test/scala-3/zio/json/DerivedDecoderSpec.scala | 8 +++++++- .../test/scala-3/zio/json/DerivedEncoderSpec.scala | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/zio-json/shared/src/main/scala/zio/json/JsonFieldDecoder.scala b/zio-json/shared/src/main/scala/zio/json/JsonFieldDecoder.scala index 68fc213cf..3d3201dee 100644 --- a/zio-json/shared/src/main/scala/zio/json/JsonFieldDecoder.scala +++ b/zio-json/shared/src/main/scala/zio/json/JsonFieldDecoder.scala @@ -40,7 +40,7 @@ trait JsonFieldDecoder[+A] { def unsafeDecodeField(trace: List[JsonError], in: String): A } -object JsonFieldDecoder { +object JsonFieldDecoder extends LowPriorityJsonFieldDecoder { def apply[A](implicit a: JsonFieldDecoder[A]): JsonFieldDecoder[A] = a implicit val string: JsonFieldDecoder[String] = new JsonFieldDecoder[String] { @@ -65,3 +65,13 @@ object JsonFieldDecoder { } } } + +private[json] trait LowPriorityJsonFieldDecoder { + + def string: JsonFieldDecoder[String] + + private def quotedString = string.map(raw => s""""$raw"""") + + implicit def stringLike[T <: String: JsonDecoder]: JsonFieldDecoder[T] = + quotedString.mapOrFail(implicitly[JsonDecoder[T]].decodeJson) +} diff --git a/zio-json/shared/src/test/scala-3/zio/json/DerivedDecoderSpec.scala b/zio-json/shared/src/test/scala-3/zio/json/DerivedDecoderSpec.scala index b7b6379f0..46a5c05a3 100644 --- a/zio-json/shared/src/test/scala-3/zio/json/DerivedDecoderSpec.scala +++ b/zio-json/shared/src/test/scala-3/zio/json/DerivedDecoderSpec.scala @@ -51,6 +51,12 @@ object DerivedDecoderSpec extends ZIOSpecDefault { assertTrue("""{"aOrB": "A", "optA": "A"}""".fromJson[Foo] == Right(Foo("A", Some("A")))) && assertTrue("""{"aOrB": "C"}""".fromJson[Foo] == Left(".aOrB(expected one of: A, B)")) - } + }, + test("Derives and decodes for a custom map key string-based union type") { + case class Foo(aOrB: Map["A" | "B", Int]) derives JsonDecoder + + assertTrue("""{"aOrB": {"A": 1, "B": 2}}""".fromJson[Foo] == Right(Foo(Map("A" -> 1, "B" -> 2)))) && + assertTrue("""{"aOrB": {"C": 1}}""".fromJson[Foo] == Left(".aOrB.C((expected one of: A, B))")) + }, ) } diff --git a/zio-json/shared/src/test/scala-3/zio/json/DerivedEncoderSpec.scala b/zio-json/shared/src/test/scala-3/zio/json/DerivedEncoderSpec.scala index 2eb329c40..2c21a599b 100644 --- a/zio-json/shared/src/test/scala-3/zio/json/DerivedEncoderSpec.scala +++ b/zio-json/shared/src/test/scala-3/zio/json/DerivedEncoderSpec.scala @@ -49,6 +49,11 @@ object DerivedEncoderSpec extends ZIOSpecDefault { case class Foo(aOrB: "A" | "B", optA: Option["A"]) derives JsonEncoder assertTrue(Foo("A", Some("A")).toJson == """{"aOrB":"A","optA":"A"}""") + }, + test("Derives and encodes for a custom map key string-based union type") { + case class Foo(aOrB: Map["A" | "B", Int]) derives JsonEncoder + + assertTrue(Foo(Map("A" -> 1, "B" -> 2)).toJson == """{"aOrB":{"A":1,"B":2}}""") } ) }