Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: mapOrFail should use underlying decoder for allowMissingValueDecoder checks #1283

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 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,6 +297,7 @@ 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
Expand Down
1 change: 1 addition & 0 deletions zio-json/shared/src/main/scala-3/zio/json/macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ 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
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