diff --git a/zio-schema-json/shared/src/main/scala/zio/schema/codec/JsonCodec.scala b/zio-schema-json/shared/src/main/scala/zio/schema/codec/JsonCodec.scala index 38ea34ff6..00440b588 100644 --- a/zio-schema-json/shared/src/main/scala/zio/schema/codec/JsonCodec.scala +++ b/zio-schema-json/shared/src/main/scala/zio/schema/codec/JsonCodec.scala @@ -1089,7 +1089,7 @@ object JsonCodec { case e: Throwable => throw new RuntimeException(s"Failed to encode field '${s.name}' in $schema'", e) } val value = s.get(a) - if (!enc.isNothing(value) && !isEmptyOptionalValue(s, value, cfg)) { + if (!isEmptyOptionalValue(s, value, cfg)) { if (first) first = false else { diff --git a/zio-schema-json/shared/src/test/scala/zio/schema/codec/JsonCodecSpec.scala b/zio-schema-json/shared/src/test/scala/zio/schema/codec/JsonCodecSpec.scala index 23d46c157..47487ab52 100644 --- a/zio-schema-json/shared/src/test/scala/zio/schema/codec/JsonCodecSpec.scala +++ b/zio-schema-json/shared/src/test/scala/zio/schema/codec/JsonCodecSpec.scala @@ -8,7 +8,7 @@ import zio.Console._ import zio._ import zio.json.JsonDecoder.JsonError import zio.json.ast.Json -import zio.json.{ DeriveJsonEncoder, JsonEncoder } +import zio.json.{ DeriveJsonEncoder, JsonEncoder, jsonExplicitNull } import zio.schema.CaseSet._ import zio.schema._ import zio.schema.annotation._ @@ -118,33 +118,41 @@ object JsonCodecSpec extends ZIOSpecDefault { suite("empty collections config")( test("list empty") { assertEncodesJson( - Schema[ListAndMap], - ListAndMap(Nil, Map("foo" -> 1)), - """{"map":{"foo":1}}""", + Schema[ListAndMapAndOption], + ListAndMapAndOption(Nil, Map("foo" -> 1), Some("foo")), + """{"map":{"foo":1},"option":"foo"}""", JsonCodec.Config(ignoreEmptyCollections = true) ) }, test("map empty") { assertEncodesJson( - Schema[ListAndMap], - ListAndMap(List("foo"), Map.empty), - """{"list":["foo"]}""", + Schema[ListAndMapAndOption], + ListAndMapAndOption(List("foo"), Map.empty, Some("foo")), + """{"list":["foo"],"option":"foo"}""", + JsonCodec.Config(ignoreEmptyCollections = true) + ) + }, + test("option empty") { + assertEncodesJson( + Schema[ListAndMapAndOption], + ListAndMapAndOption(List("foo"), Map("foo" -> 1), None), + """{"list":["foo"],"map":{"foo":1}}""", JsonCodec.Config(ignoreEmptyCollections = true) ) }, test("all empty") { assertEncodesJson( - Schema[ListAndMap], - ListAndMap(Nil, Map.empty), + Schema[ListAndMapAndOption], + ListAndMapAndOption(Nil, Map.empty, None), """{}""", JsonCodec.Config(ignoreEmptyCollections = true) ) }, test("all empty, but don't ignore empty collections") { assertEncodesJson( - Schema[ListAndMap], - ListAndMap(Nil, Map.empty), - """{"list":[],"map":{}}""", + Schema[ListAndMapAndOption], + ListAndMapAndOption(Nil, Map.empty, None), + """{"list":[],"map":{},"option":null}""", JsonCodec.Config(ignoreEmptyCollections = false) ) } @@ -269,7 +277,7 @@ object JsonCodecSpec extends ZIOSpecDefault { assertEncodes( WithOptionFields.schema, WithOptionFields(Some("s"), None), - charSequenceToByteChunk("""{"a":"s"}""") + charSequenceToByteChunk("""{"a":"s","b":null}""") ) } ), @@ -327,7 +335,7 @@ object JsonCodecSpec extends ZIOSpecDefault { assertEncodes( Subscription.schema, Subscription.Unlimited(None), - charSequenceToByteChunk("""{"type":"unlimited"}""") + charSequenceToByteChunk("""{"type":"unlimited","until":null}""") ) }, suite("with no discriminator")( @@ -871,7 +879,7 @@ object JsonCodecSpec extends ZIOSpecDefault { assertDecodes( WithOptionFields.schema, WithOptionFields(Some("s"), None), - charSequenceToByteChunk("""{"a":"s"}""") + charSequenceToByteChunk("""{"a":"s","b":null}""") ) }, test("case class with option fields accept empty json object as value") { @@ -1510,8 +1518,7 @@ object JsonCodecSpec extends ZIOSpecDefault { Enumeration3(StringValue3("foo")) ) &> assertEncodesThenDecodes( Schema[Enumeration3], - Enumeration3(StringValue3Multi("foo", "bar")), - print = true + Enumeration3(StringValue3Multi("foo", "bar")) ) }, test("of case classes with discriminator") { @@ -1878,6 +1885,7 @@ object JsonCodecSpec extends ZIOSpecDefault { charSequenceToByteChunk(encoded) } + @jsonExplicitNull case class SearchRequest(query: String, pageNumber: Int, resultPerPage: Int, nextPage: Option[String]) object SearchRequest { @@ -2159,10 +2167,10 @@ object JsonCodecSpec extends ZIOSpecDefault { implicit lazy val schema: Schema[WithOptField] = DeriveSchema.gen[WithOptField] } - final case class ListAndMap(list: List[String], map: Map[String, Int]) + final case class ListAndMapAndOption(list: List[String], map: Map[String, Int], option: Option[String]) - object ListAndMap { - implicit lazy val schema: Schema[ListAndMap] = DeriveSchema.gen[ListAndMap] + object ListAndMapAndOption { + implicit lazy val schema: Schema[ListAndMapAndOption] = DeriveSchema.gen[ListAndMapAndOption] } final case class KeyWrapper(key: String)