Skip to content

Commit

Permalink
Fix duplicated field rejection for JSON codecs of records (#765)
Browse files Browse the repository at this point in the history
  • Loading branch information
plokhotnyuk authored Jan 4, 2025
1 parent ffe4808 commit e1759e3
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,7 @@ object JsonCodec {
val rejectAdditionalFields = schema.annotations.exists(_.isInstanceOf[rejectExtraFields])
(trace: List[JsonError], in: RetractReader) => {
Lexer.char(trace, in, '{')
val map = defaults.clone().asInstanceOf[util.HashMap[String, Any]]
val map = new util.HashMap[String, Any]
var continue = Lexer.firstField(trace, in)
while (continue) {
val fieldNameOrAlias = Lexer.string(trace, in).toString
Expand All @@ -1013,7 +1013,10 @@ object JsonCodec {
val trace_ = span :: trace
Lexer.char(trace_, in, ':')
val fieldName = span.field
map.put(fieldName, dec.unsafeDecode(trace_, in))
val prev = map.put(fieldName, dec.unsafeDecode(trace_, in))
if (prev != null) {
throw UnsafeJson(JsonError.Message("duplicate") :: trace)
}
} else if (rejectAdditionalFields) {
throw UnsafeJson(JsonError.Message(s"unexpected field: $fieldNameOrAlias") :: trace)
} else {
Expand All @@ -1022,6 +1025,11 @@ object JsonCodec {
}
continue = Lexer.nextField(trace, in)
}
val it = defaults.entrySet().iterator()
while (it.hasNext) {
val entry = it.next()
map.putIfAbsent(entry.getKey, entry.getValue)
}
(ListMap.newBuilder[String, Any] ++= ({ // to avoid O(n) insert operations
import scala.collection.JavaConverters.mapAsScalaMapConverter // use deprecated class for Scala 2.12 compatibility

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,18 @@ object JsonCodecSpec extends ZIOSpecDefault {
charSequenceToByteChunk("""{"$f1":"test", "extraField":"extra"}""")
).flip.map(err => assertTrue(err.getMessage() == "(unexpected field: extraField)"))
},
test("reject duplicated fields") {
assertDecodesToError(
recordSchema,
"""{"foo":"test","bar":10,"foo":10}""",
JsonError.Message("duplicate") :: Nil
) &>
assertDecodesToError(
recordSchema,
"""{"bar":10,"foo":"test","bar":"100"}""",
JsonError.Message("duplicate") :: Nil
)
},
test("optional field with schema or annotated default value") {
assertDecodes(
RecordExampleWithOptField.schema,
Expand Down Expand Up @@ -812,6 +824,18 @@ object JsonCodecSpec extends ZIOSpecDefault {
JsonError.Message("extra field") :: Nil
)
},
test("reject duplicated fields") {
assertDecodesToError(
personSchema,
"""{"name":"test","age":10,"name":10}""",
JsonError.Message("duplicate") :: Nil
) &>
assertDecodesToError(
personSchema,
"""{"age":10,"name":"test","age":"100"}""",
JsonError.Message("duplicate") :: Nil
)
},
test("transient field annotation with default value in class definition") {
assertDecodes(
searchRequestWithTransientFieldSchema,
Expand Down

0 comments on commit e1759e3

Please sign in to comment.