Skip to content

Commit

Permalink
generate empty collections for missing fields (#741)
Browse files Browse the repository at this point in the history
  • Loading branch information
juliano authored Sep 18, 2024
1 parent 0ef6321 commit 0e69702
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1523,12 +1523,21 @@ object JsonCodec {
var i = 0
while (i < len) {
if (buffer(i) == null) {

if ((fields(i).optional || fields(i).transient) && fields(i).defaultValue.isDefined)
if ((fields(i).optional || fields(i).transient) && fields(i).defaultValue.isDefined) {
buffer(i) = fields(i).defaultValue.get
else
buffer(i) = schemaDecoder(schemas(i)).unsafeDecodeMissing(spans(i) :: trace)
} else {
val schema = fields(i).schema match {
case l @ Schema.Lazy(_) => l.schema
case _ => schemas(i)
}

schema match {
case collection: Schema.Collection[_, _] =>
buffer(i) = collection.empty
case _ =>
buffer(i) = schemaDecoder(schema).unsafeDecodeMissing(spans(i) :: trace)
}
}
}
i += 1
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,43 @@ object JsonCodecSpec extends ZIOSpecDefault {
)
}
),
suite("Missing collection fields")(
test("map") {
assertDecodes(
Schema[ListAndMapAndOption],
ListAndMapAndOption(Nil, Map.empty, None),
charSequenceToByteChunk("""{"list":[]}""")
)
},
test("list") {
assertDecodes(
Schema[ListAndMapAndOption],
ListAndMapAndOption(Nil, Map.empty, None),
charSequenceToByteChunk("""{"map":{}}""")
)
},
test("set") {
assertDecodes(
Schema[SetWrapper],
SetWrapper(Set.empty),
charSequenceToByteChunk("""{}""")
)
},
test("vector") {
assertDecodes(
Schema[VectorWrapper],
VectorWrapper(Vector.empty),
charSequenceToByteChunk("""{}""")
)
},
test("chunck") {
assertDecodes(
Schema[ChunckWrapper],
ChunckWrapper(Chunk.empty),
charSequenceToByteChunk("""{}""")
)
}
),
suite("zio.json.ast.Json decoding")(
test("Json.Obj") {
assertDecodes(
Expand Down Expand Up @@ -2172,6 +2209,24 @@ object JsonCodecSpec extends ZIOSpecDefault {
implicit lazy val schema: Schema[ListAndMapAndOption] = DeriveSchema.gen[ListAndMapAndOption]
}

final case class SetWrapper(set: Set[String])

object SetWrapper {
implicit lazy val schema: Schema[SetWrapper] = DeriveSchema.gen[SetWrapper]
}

final case class VectorWrapper(sequence: Vector[String])

object VectorWrapper {
implicit lazy val schema: Schema[VectorWrapper] = DeriveSchema.gen[VectorWrapper]
}

final case class ChunckWrapper(chunk: Chunk[String])

object ChunckWrapper {
implicit lazy val schema: Schema[ChunckWrapper] = DeriveSchema.gen[ChunckWrapper]
}

final case class KeyWrapper(key: String)

object KeyWrapper {
Expand Down
9 changes: 9 additions & 0 deletions zio-schema/shared/src/main/scala/zio/schema/Schema.scala
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ object Schema extends SchemaPlatformSpecific with SchemaEquality {
sealed trait Collection[Col, Elem] extends Schema[Col] {
def fromChunk: Chunk[Elem] => Col
def toChunk: Col => Chunk[Elem]
def empty: Col
}

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

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

override def empty: Col = fromChunk(Chunk.empty[Elem])
}

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

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

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

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

override def makeAccessors(b: AccessorBuilder): b.Traversal[prelude.NonEmptyMap[K, V], (K, V)] =
b.makeTraversal(self, keySchema <*> valueSchema)

override def empty: prelude.NonEmptyMap[K, V] =
throw new IllegalArgumentException("NonEmptyMap cannot be empty")
}

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

override def toString: String = s"NonEmptySequence($elementSchema, $identity)"

override def empty: Col = throw new IllegalArgumentException(s"NonEmptySequence $identity cannot be empty")
}

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

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

override def empty: scala.collection.immutable.Set[A] = scala.collection.immutable.Set.empty[A]
}

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

0 comments on commit 0e69702

Please sign in to comment.