Skip to content

Commit 0e69702

Browse files
authored
generate empty collections for missing fields (#741)
1 parent 0ef6321 commit 0e69702

File tree

3 files changed

+77
-4
lines changed

3 files changed

+77
-4
lines changed

zio-schema-json/shared/src/main/scala/zio/schema/codec/JsonCodec.scala

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,12 +1523,21 @@ object JsonCodec {
15231523
var i = 0
15241524
while (i < len) {
15251525
if (buffer(i) == null) {
1526-
1527-
if ((fields(i).optional || fields(i).transient) && fields(i).defaultValue.isDefined)
1526+
if ((fields(i).optional || fields(i).transient) && fields(i).defaultValue.isDefined) {
15281527
buffer(i) = fields(i).defaultValue.get
1529-
else
1530-
buffer(i) = schemaDecoder(schemas(i)).unsafeDecodeMissing(spans(i) :: trace)
1528+
} else {
1529+
val schema = fields(i).schema match {
1530+
case l @ Schema.Lazy(_) => l.schema
1531+
case _ => schemas(i)
1532+
}
15311533

1534+
schema match {
1535+
case collection: Schema.Collection[_, _] =>
1536+
buffer(i) = collection.empty
1537+
case _ =>
1538+
buffer(i) = schemaDecoder(schema).unsafeDecodeMissing(spans(i) :: trace)
1539+
}
1540+
}
15321541
}
15331542
i += 1
15341543
}

zio-schema-json/shared/src/test/scala/zio/schema/codec/JsonCodecSpec.scala

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,43 @@ object JsonCodecSpec extends ZIOSpecDefault {
10511051
)
10521052
}
10531053
),
1054+
suite("Missing collection fields")(
1055+
test("map") {
1056+
assertDecodes(
1057+
Schema[ListAndMapAndOption],
1058+
ListAndMapAndOption(Nil, Map.empty, None),
1059+
charSequenceToByteChunk("""{"list":[]}""")
1060+
)
1061+
},
1062+
test("list") {
1063+
assertDecodes(
1064+
Schema[ListAndMapAndOption],
1065+
ListAndMapAndOption(Nil, Map.empty, None),
1066+
charSequenceToByteChunk("""{"map":{}}""")
1067+
)
1068+
},
1069+
test("set") {
1070+
assertDecodes(
1071+
Schema[SetWrapper],
1072+
SetWrapper(Set.empty),
1073+
charSequenceToByteChunk("""{}""")
1074+
)
1075+
},
1076+
test("vector") {
1077+
assertDecodes(
1078+
Schema[VectorWrapper],
1079+
VectorWrapper(Vector.empty),
1080+
charSequenceToByteChunk("""{}""")
1081+
)
1082+
},
1083+
test("chunck") {
1084+
assertDecodes(
1085+
Schema[ChunckWrapper],
1086+
ChunckWrapper(Chunk.empty),
1087+
charSequenceToByteChunk("""{}""")
1088+
)
1089+
}
1090+
),
10541091
suite("zio.json.ast.Json decoding")(
10551092
test("Json.Obj") {
10561093
assertDecodes(
@@ -2172,6 +2209,24 @@ object JsonCodecSpec extends ZIOSpecDefault {
21722209
implicit lazy val schema: Schema[ListAndMapAndOption] = DeriveSchema.gen[ListAndMapAndOption]
21732210
}
21742211

2212+
final case class SetWrapper(set: Set[String])
2213+
2214+
object SetWrapper {
2215+
implicit lazy val schema: Schema[SetWrapper] = DeriveSchema.gen[SetWrapper]
2216+
}
2217+
2218+
final case class VectorWrapper(sequence: Vector[String])
2219+
2220+
object VectorWrapper {
2221+
implicit lazy val schema: Schema[VectorWrapper] = DeriveSchema.gen[VectorWrapper]
2222+
}
2223+
2224+
final case class ChunckWrapper(chunk: Chunk[String])
2225+
2226+
object ChunckWrapper {
2227+
implicit lazy val schema: Schema[ChunckWrapper] = DeriveSchema.gen[ChunckWrapper]
2228+
}
2229+
21752230
final case class KeyWrapper(key: String)
21762231

21772232
object KeyWrapper {

zio-schema/shared/src/main/scala/zio/schema/Schema.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ object Schema extends SchemaPlatformSpecific with SchemaEquality {
512512
sealed trait Collection[Col, Elem] extends Schema[Col] {
513513
def fromChunk: Chunk[Elem] => Col
514514
def toChunk: Col => Chunk[Elem]
515+
def empty: Col
515516
}
516517

517518
final case class Sequence[Col, Elem, I](
@@ -534,6 +535,7 @@ object Schema extends SchemaPlatformSpecific with SchemaEquality {
534535

535536
override def toString: String = s"Sequence($elementSchema, $identity)"
536537

538+
override def empty: Col = fromChunk(Chunk.empty[Elem])
537539
}
538540

539541
final case class Transform[A, B, I](
@@ -840,6 +842,7 @@ object Schema extends SchemaPlatformSpecific with SchemaEquality {
840842

841843
override val toChunk: scala.collection.immutable.Map[K, V] => Chunk[(K, V)] = map => Chunk.fromIterable(map.toList)
842844

845+
override def empty: scala.collection.immutable.Map[K, V] = scala.collection.immutable.Map.empty[K, V]
843846
}
844847

845848
final case class NonEmptyMap[K, V](
@@ -874,6 +877,9 @@ object Schema extends SchemaPlatformSpecific with SchemaEquality {
874877

875878
override def makeAccessors(b: AccessorBuilder): b.Traversal[prelude.NonEmptyMap[K, V], (K, V)] =
876879
b.makeTraversal(self, keySchema <*> valueSchema)
880+
881+
override def empty: prelude.NonEmptyMap[K, V] =
882+
throw new IllegalArgumentException("NonEmptyMap cannot be empty")
877883
}
878884

879885
final case class NonEmptySequence[Col, Elm, I](
@@ -900,6 +906,8 @@ object Schema extends SchemaPlatformSpecific with SchemaEquality {
900906
override def makeAccessors(b: AccessorBuilder): b.Traversal[Col, Elm] = b.makeTraversal(self, elementSchema)
901907

902908
override def toString: String = s"NonEmptySequence($elementSchema, $identity)"
909+
910+
override def empty: Col = throw new IllegalArgumentException(s"NonEmptySequence $identity cannot be empty")
903911
}
904912

905913
final case class Set[A](elementSchema: Schema[A], override val annotations: Chunk[Any] = Chunk.empty)
@@ -923,6 +931,7 @@ object Schema extends SchemaPlatformSpecific with SchemaEquality {
923931

924932
override val toChunk: scala.collection.immutable.Set[A] => Chunk[A] = Chunk.fromIterable(_)
925933

934+
override def empty: scala.collection.immutable.Set[A] = scala.collection.immutable.Set.empty[A]
926935
}
927936

928937
final case class Dynamic(override val annotations: Chunk[Any] = Chunk.empty) extends Schema[DynamicValue] {

0 commit comments

Comments
 (0)