Skip to content

Commit

Permalink
fix: mapOrFail should use underlying decoder for allowMissingValueDec…
Browse files Browse the repository at this point in the history
…oder checks
  • Loading branch information
ThijsBroersen committed Feb 3, 2025
1 parent d7969a7 commit 4caf392
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 4 deletions.
3 changes: 2 additions & 1 deletion zio-json/shared/src/main/scala-2.x/zio/json/macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,10 @@ object DeriveJsonDecoder {

@tailrec
private[this] def allowMissingValueDecoder(d: JsonDecoder[_]): Boolean = d match {
case _: OptionJsonDecoder[_] => true
case _: CollectionJsonDecoder[_] => !explicitEmptyCollections
case d: MappedJsonDecoder[_] => allowMissingValueDecoder(d.underlying)
case _ => true
case _ => false
}

override def unsafeDecodeMissing(trace: List[JsonError]): A = {
Expand Down
3 changes: 2 additions & 1 deletion zio-json/shared/src/main/scala-3/zio/json/macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,10 @@ sealed class JsonDecoderDerivation(config: JsonCodecConfiguration) extends Deriv

@tailrec
private[this] def allowMissingValueDecoder(d: JsonDecoder[_]): Boolean = d match {
case _: OptionJsonDecoder[_] => true
case _: CollectionJsonDecoder[_] => !explicitEmptyCollections
case d: MappedJsonDecoder[_] => allowMissingValueDecoder(d.underlying)
case _ => true
case _ => false
}

override def unsafeDecodeMissing(trace: List[JsonError]): A = {
Expand Down
8 changes: 6 additions & 2 deletions zio-json/shared/src/main/scala/zio/json/JsonDecoder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ trait JsonDecoder[A] extends JsonDecoderPlatformSpecific[A] {
* with some type of error.
*/
final def mapOrFail[B](f: A => Either[String, B]): JsonDecoder[B] =
new JsonDecoder[B] {
new MappedJsonDecoder[B] {
private[json] def underlying: JsonDecoder[A] = self

def unsafeDecode(trace: List[JsonError], in: RetractReader): B =
f(self.unsafeDecode(trace, in)) match {
case Right(b) => b
Expand Down Expand Up @@ -254,7 +256,9 @@ object JsonDecoder extends GeneratedTupleDecoders with DecoderLowPriority1 with
}

def suspend[A](decoder0: => JsonDecoder[A]): JsonDecoder[A] =
new JsonDecoder[A] {
new MappedJsonDecoder[A] {
private[json] def underlying: JsonDecoder[A] = decoder0

lazy val decoder = decoder0

override def unsafeDecode(trace: List[JsonError], in: RetractReader): A = decoder.unsafeDecode(trace, in)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,48 @@ object ConfigurableDeriveCodecSpec extends ZIOSpecDefault {
implicit val codec: JsonCodec[EmptyListMap] = DeriveJsonCodec.gen

assertTrue("""{}""".fromJson[EmptyListMap].toOption.contains(expectedObj), expectedObj.toJson == expectedStr)
},
test("for a transform collection") {
case class MappedCollection(a: List[Int])
case class EmptyMappedCollection(a: MappedCollection)
val expectedStr = """{"a":[]}"""
val expectedObj = EmptyMappedCollection(MappedCollection(List.empty))

implicit val config: JsonCodecConfiguration =
JsonCodecConfiguration(explicitEmptyCollections = ExplicitEmptyCollections(decoding = false))
implicit val codec: JsonCodec[MappedCollection] = JsonCodec
.list[Int]
.transform(
v => MappedCollection(v),
_.a
)
implicit val emptyMappedCollectionCodec: JsonCodec[EmptyMappedCollection] = DeriveJsonCodec.gen

assertTrue(
"""{}""".fromJson[EmptyMappedCollection].toOption.contains(expectedObj),
expectedObj.toJson == expectedStr
)
},
test("for a transformOrFail collection") {
case class MappedCollection(a: List[Int])
case class EmptyMappedCollection(a: MappedCollection)
val expectedStr = """{"a":[]}"""
val expectedObj = EmptyMappedCollection(MappedCollection(List.empty))

implicit val config: JsonCodecConfiguration =
JsonCodecConfiguration(explicitEmptyCollections = ExplicitEmptyCollections(decoding = false))
implicit val codec: JsonCodec[MappedCollection] = JsonCodec
.list[Int]
.transformOrFail(
v => Right(MappedCollection(v)),
_.a
)
implicit val emptyMappedCollectionCodec: JsonCodec[EmptyMappedCollection] = DeriveJsonCodec.gen

assertTrue(
"""{}""".fromJson[EmptyMappedCollection].toOption.contains(expectedObj),
expectedObj.toJson == expectedStr
)
}
),
suite("should not write empty collections and fail missing empty collections")(
Expand Down Expand Up @@ -768,6 +810,42 @@ object ConfigurableDeriveCodecSpec extends ZIOSpecDefault {
implicit val codec: JsonCodec[EmptyListMap] = DeriveJsonCodec.gen

assertTrue(expectedStr.fromJson[EmptyListMap].isLeft, expectedObj.toJson == expectedStr)
},
test("for a transform collection") {
case class MappedCollection(a: List[Int])
case class EmptyMappedCollection(a: MappedCollection)
val expectedStr = """{}"""
val expectedObj = EmptyMappedCollection(MappedCollection(List.empty))

implicit val config: JsonCodecConfiguration =
JsonCodecConfiguration(explicitEmptyCollections = ExplicitEmptyCollections(false))
implicit val codec: JsonCodec[MappedCollection] = JsonCodec
.list[Int]
.transform(
v => MappedCollection(v),
_.a
)
implicit val emptyMappedCollectionCodec: JsonCodec[EmptyMappedCollection] = DeriveJsonCodec.gen

assertTrue(expectedStr.fromJson[EmptyMappedCollection].isLeft, expectedObj.toJson == expectedStr)
},
test("for a transformOrFail collection") {
case class MappedCollection(a: List[Int])
case class EmptyMappedCollection(a: MappedCollection)
val expectedStr = """{}"""
val expectedObj = EmptyMappedCollection(MappedCollection(List.empty))

implicit val config: JsonCodecConfiguration =
JsonCodecConfiguration(explicitEmptyCollections = ExplicitEmptyCollections(false))
implicit val codec: JsonCodec[MappedCollection] = JsonCodec
.list[Int]
.transformOrFail(
v => Right(MappedCollection(v)),
_.a
)
implicit val emptyMappedCollectionCodec: JsonCodec[EmptyMappedCollection] = DeriveJsonCodec.gen

assertTrue(expectedStr.fromJson[EmptyMappedCollection].isLeft, expectedObj.toJson == expectedStr)
}
)
)
Expand Down

0 comments on commit 4caf392

Please sign in to comment.