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 b72d8af6d..586860b04 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 @@ -991,7 +991,29 @@ object JsonCodec { import JsonCodec.JsonDecoder.schemaDecoder private[codec] def caseClass0Decoder[Z](discriminator: Int, schema: Schema.CaseClass0[Z]): ZJsonDecoder[Z] = { (trace: List[JsonError], in: RetractReader) => - if (discriminator == -1) Codecs.unitDecoder.unsafeDecode(trace, in) + def skipField(): Unit = { + val rejectExtraFields = schema.annotations.collectFirst({ case _: rejectExtraFields => () }).isDefined + if (rejectExtraFields) { + throw UnsafeJson(JsonError.Message("extra field") :: trace) + } + Lexer.char(trace, in, '"') + Lexer.skipString(trace, in) + Lexer.char(trace, in, ':') + Lexer.skipValue(trace, in) + } + + if (discriminator == -2) { + while (Lexer.nextField(trace, in)) { skipField() } + } else { + if (discriminator == -1) { + Lexer.char(trace, in, '{') + } + if (Lexer.firstField(trace, in)) { + skipField() + while (Lexer.nextField(trace, in)) { skipField() } + } + } + schema.defaultConstruct() } diff --git a/zio-schema-json/shared/src/test/scala-2/zio/schema/codec/JsonCodecSpec.scala b/zio-schema-json/shared/src/test/scala-2/zio/schema/codec/JsonCodecSpec.scala index 115bee8e5..e4957af48 100644 --- a/zio-schema-json/shared/src/test/scala-2/zio/schema/codec/JsonCodecSpec.scala +++ b/zio-schema-json/shared/src/test/scala-2/zio/schema/codec/JsonCodecSpec.scala @@ -524,7 +524,12 @@ object JsonCodecSpec extends ZIOSpecDefault { PersonWithRejectExtraFields.schema, """{"name":"test","age":10,"extraField":10}""", JsonError.Message("extra field") :: Nil - ) + ) &> + assertDecodesToError( + schemaObject.annotate(rejectExtraFields()), + """{"extraField":10}""", + JsonError.Message("extra field") :: Nil + ) }, test("transient field annotation") { assertDecodes( @@ -1242,6 +1247,13 @@ object JsonCodecSpec extends ZIOSpecDefault { assertEncodesThenDecodes(Schema[Command], Command.Cash) &> assertEncodesThenDecodes(Schema[Command], Command.Buy(100)) }, + test("decode discriminated case objects in array")( + assertDecodes(Schema[List[Command]], Command.Cash :: Nil, charSequenceToByteChunk("""[{"type":"Cash"}]""")) + ), + test("decode discriminated case objects with extra fields")( + assertDecodes(Schema[Command], Command.Cash, charSequenceToByteChunk("""{"type":"Cash","extraField":1}""")) &> + assertDecodes(Schema[Command], Command.Cash, charSequenceToByteChunk("""{"extraField":1,"type":"Cash"}"""")) + ), suite("of case objects")( test("without annotation")( assertEncodesThenDecodes(Schema[Color], Color.Red) @@ -1593,6 +1605,7 @@ object JsonCodecSpec extends ZIOSpecDefault { val schema: Schema[PersonWithRejectExtraFields] = DeriveSchema.gen[PersonWithRejectExtraFields] } + case class FieldDefaultValueSearchRequest( query: String, pageNumber: Int, diff --git a/zio-schema-json/shared/src/test/scala-3/zio/schema/codec/DefaultValueSpec.scala b/zio-schema-json/shared/src/test/scala-3/zio/schema/codec/DefaultValueSpec.scala index cd65b6a00..4b2dd94bc 100644 --- a/zio-schema-json/shared/src/test/scala-3/zio/schema/codec/DefaultValueSpec.scala +++ b/zio-schema-json/shared/src/test/scala-3/zio/schema/codec/DefaultValueSpec.scala @@ -12,7 +12,7 @@ object DefaultValueSpec extends ZIOSpecDefault { def spec: Spec[TestEnvironment, Any] = suite("Custom Spec")( - customSuite, + customSuite ) @@ timeout(90.seconds) private val customSuite = suite("custom")( @@ -24,7 +24,7 @@ object DefaultValueSpec extends ZIOSpecDefault { ) ) - case class WithDefaultValue(orderId:Int, description: String = "desc") + case class WithDefaultValue(orderId: Int, description: String = "desc") object WithDefaultValue { implicit lazy val schema: Schema[WithDefaultValue] = DeriveSchema.gen[WithDefaultValue]