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 JSON decoding of empty objects #710

Merged
merged 3 commits into from
Jul 9, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calculating this every time may seem redundant, but skipField() won't run at all in most cases.

Copy link
Contributor

@987Nabil 987Nabil Jul 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we can make this a val on Schema.Record. Maybe I should do this in my PR

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()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1593,6 +1605,7 @@ object JsonCodecSpec extends ZIOSpecDefault {

val schema: Schema[PersonWithRejectExtraFields] = DeriveSchema.gen[PersonWithRejectExtraFields]
}

case class FieldDefaultValueSearchRequest(
query: String,
pageNumber: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")(
Expand All @@ -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]
Expand Down
Loading