Skip to content

Commit e980989

Browse files
committed
More efficient JSON codecs for sets and maps
1 parent a59764e commit e980989

File tree

1 file changed

+34
-44
lines changed

1 file changed

+34
-44
lines changed

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

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -330,24 +330,23 @@ object JsonCodec {
330330
//scalafmt: { maxColumn = 400, optIn.configStyleArguments = false }
331331
private[this] def schemaEncoderSlow[A](schema: Schema[A], cfg: Config, discriminatorTuple: DiscriminatorTuple): ZJsonEncoder[A] =
332332
schema match {
333-
case Schema.Primitive(standardType, _) => primitiveCodec(standardType).encoder
334-
case Schema.Sequence(schema, _, g, _, _) => ZJsonEncoder.chunk(schemaEncoder(schema, cfg, discriminatorTuple)).contramap(g)
335-
case Schema.NonEmptySequence(schema, _, g, _, _) => ZJsonEncoder.chunk(schemaEncoder(schema, cfg, discriminatorTuple)).contramap(g)
336-
case Schema.Map(ks, vs, _) => mapEncoder(ks, vs, discriminatorTuple, cfg)
337-
case Schema.NonEmptyMap(ks: Schema[kt], vs: Schema[vt], _) => mapEncoder(ks, vs, discriminatorTuple, cfg).contramap[NonEmptyMap[kt, vt]](_.toMap.asInstanceOf[Map[kt, vt]]).asInstanceOf[ZJsonEncoder[A]]
338-
case Schema.Set(s, _) =>
339-
ZJsonEncoder.chunk(schemaEncoder(s, cfg, discriminatorTuple)).contramap(m => Chunk.fromIterable(m))
340-
case Schema.Transform(c, _, g, a, _) => transformEncoder(a.foldLeft(c)((s, a) => s.annotate(a)), g, cfg, discriminatorTuple)
341-
case Schema.Tuple2(l, r, _) => ZJsonEncoder.tuple2(schemaEncoder(l, cfg, discriminatorTuple), schemaEncoder(r, cfg, discriminatorTuple))
342-
case Schema.Optional(schema, _) => ZJsonEncoder.option(schemaEncoder(schema, cfg, discriminatorTuple))
343-
case Schema.Fail(_, _) => unitEncoder.contramap(_ => ())
344-
case s: Schema.GenericRecord => recordEncoder(s, cfg, discriminatorTuple)
345-
case Schema.Either(left, right, _) => ZJsonEncoder.either(schemaEncoder(left, cfg, discriminatorTuple), schemaEncoder(right, cfg, discriminatorTuple))
346-
case Schema.Fallback(left, right, _, _) => fallbackEncoder(schemaEncoder(left, cfg, discriminatorTuple), schemaEncoder(right, cfg, discriminatorTuple))
347-
case l @ Schema.Lazy(_) => ZJsonEncoder.suspend(schemaEncoder(l.schema, cfg, discriminatorTuple))
348-
case s: Schema.Record[A] => caseClassEncoder(s, cfg, discriminatorTuple)
349-
case s: Schema.Enum[A] => enumEncoder(s, cfg)
350-
case d @ Schema.Dynamic(_) => dynamicEncoder(d, cfg)
333+
case Schema.Primitive(standardType, _) => primitiveCodec(standardType).encoder
334+
case Schema.Optional(schema, _) => ZJsonEncoder.option(schemaEncoder(schema, cfg))
335+
case Schema.Tuple2(l, r, _) => ZJsonEncoder.tuple2(schemaEncoder(l, cfg), schemaEncoder(r, cfg))
336+
case Schema.Sequence(schema, _, g, _, _) => ZJsonEncoder.chunk(schemaEncoder(schema, cfg)).contramap(g)
337+
case Schema.NonEmptySequence(schema, _, g, _, _) => ZJsonEncoder.chunk(schemaEncoder(schema, cfg)).contramap(g)
338+
case Schema.Map(ks, vs, _) => mapEncoder(ks, vs, cfg)
339+
case Schema.NonEmptyMap(ks, vs, _) => mapEncoder(ks, vs, cfg).contramap(_.toMap)
340+
case Schema.Set(s, _) => ZJsonEncoder.set(schemaEncoder(s, cfg))
341+
case Schema.Transform(c, _, g, a, _) => transformEncoder(a.foldLeft(c)((s, a) => s.annotate(a)), g, cfg, discriminatorTuple)
342+
case Schema.Fail(_, _) => unitEncoder.contramap(_ => ())
343+
case Schema.Either(left, right, _) => ZJsonEncoder.either(schemaEncoder(left, cfg), schemaEncoder(right, cfg))
344+
case Schema.Fallback(left, right, _, _) => fallbackEncoder(schemaEncoder(left, cfg), schemaEncoder(right, cfg))
345+
case l @ Schema.Lazy(_) => ZJsonEncoder.suspend(schemaEncoder(l.schema, cfg))
346+
case s: Schema.GenericRecord => recordEncoder(s, cfg, discriminatorTuple)
347+
case s: Schema.Record[A] => caseClassEncoder(s, cfg, discriminatorTuple)
348+
case s: Schema.Enum[A] => enumEncoder(s, cfg)
349+
case d @ Schema.Dynamic(_) => dynamicEncoder(d, cfg)
351350
case null =>
352351
throw new Exception(s"A captured schema is null, most likely due to wrong field initialization order")
353352
}
@@ -361,8 +360,8 @@ object JsonCodec {
361360
new JsonFieldEncoder[B] {
362361
override def unsafeEncodeField(b: B): String =
363362
g(b) match {
364-
case Left(_) => throw new RuntimeException(s"Failed to encode field $b")
365363
case Right(a) => fieldEncoder.unsafeEncodeField(a)
364+
case _ => throw new RuntimeException(s"Failed to encode field $b")
366365
}
367366
}
368367
}
@@ -380,27 +379,19 @@ object JsonCodec {
380379
private[codec] def mapEncoder[K, V](
381380
ks: Schema[K],
382381
vs: Schema[V],
383-
discriminatorTuple: DiscriminatorTuple,
384382
cfg: Config
385383
): ZJsonEncoder[Map[K, V]] = {
386384
val valueEncoder = JsonEncoder.schemaEncoder(vs, cfg)
387385
jsonFieldEncoder(ks) match {
388386
case Some(jsonFieldEncoder) =>
389-
ZJsonEncoder.keyValueIterable(jsonFieldEncoder, valueEncoder).contramap(a => Chunk.fromIterable(a).toMap)
387+
ZJsonEncoder.map(jsonFieldEncoder, valueEncoder)
390388
case None =>
391-
ZJsonEncoder
392-
.chunk(schemaEncoder(ks, cfg, discriminatorTuple).zip(schemaEncoder(vs, cfg, discriminatorTuple)))
393-
.contramap(m => Chunk.fromIterable(m))
389+
ZJsonEncoder.chunk(schemaEncoder(ks, cfg).zip(valueEncoder)).contramap(Chunk.fromIterable)
394390
}
395391
}
396392

397-
private[codec] def dynamicEncoder(schema: Schema.Dynamic, cfg: JsonCodec.Config): ZJsonEncoder[DynamicValue] = {
398-
val directMapping = schema.annotations.exists {
399-
case directDynamicMapping() => true
400-
case _ => false
401-
}
402-
403-
if (directMapping) {
393+
private[codec] def dynamicEncoder(schema: Schema.Dynamic, cfg: JsonCodec.Config): ZJsonEncoder[DynamicValue] =
394+
if (schema.annotations.exists(_.isInstanceOf[directDynamicMapping])) {
404395
new ZJsonEncoder[DynamicValue] { directEncoder =>
405396
override def unsafeEncode(value: DynamicValue, indent: Option[Int], out: Write): Unit =
406397
value match {
@@ -468,7 +459,6 @@ object JsonCodec {
468459
} else {
469460
schemaEncoder(DynamicValue.schema, cfg)
470461
}
471-
}
472462

473463
private def transformEncoder[A, B](
474464
schema: Schema[A],
@@ -481,14 +471,14 @@ object JsonCodec {
481471

482472
override def unsafeEncode(b: B, indent: Option[Int], out: Write): Unit =
483473
g(b) match {
484-
case Left(_) => ()
485474
case Right(a) => innerEncoder.unsafeEncode(a, indent, out)
475+
case _ => ()
486476
}
487477

488478
override def isNothing(b: B): Boolean =
489479
g(b) match {
490-
case Left(_) => false
491480
case Right(a) => innerEncoder.isNothing(a)
481+
case _ => false
492482
}
493483
}
494484

@@ -576,11 +566,11 @@ object JsonCodec {
576566
discriminatorTuple: DiscriminatorTuple
577567
): ZJsonEncoder[ListMap[String, _]] = {
578568
val nonTransientFields = schema.nonTransientFields.toArray
579-
val encoders = nonTransientFields.map(field => schemaEncoder(field.schema.asInstanceOf[Schema[Any]], cfg))
580569
if (nonTransientFields.isEmpty) { (_: ListMap[String, _], _: Option[Int], out: Write) =>
581570
out.write("{}")
582-
} else { (value: ListMap[String, _], indent: Option[Int], out: Write) =>
583-
{
571+
} else {
572+
val encoders = nonTransientFields.map(field => schemaEncoder(field.schema.asInstanceOf[Schema[Any]], cfg))
573+
(value: ListMap[String, _], indent: Option[Int], out: Write) =>{
584574
out.write('{')
585575
val doPrettyPrint = indent ne None
586576
var indent_ = indent
@@ -687,19 +677,20 @@ object JsonCodec {
687677
//scalafmt: { maxColumn = 400, optIn.configStyleArguments = false }
688678
private[this] def schemaDecoderSlow[A](schema: Schema[A], discriminator: Option[String]): ZJsonDecoder[A] = schema match {
689679
case Schema.Primitive(standardType, _) => primitiveCodec(standardType).decoder
690-
case Schema.Optional(codec, _) => option(schemaDecoder(codec, discriminator))
680+
case Schema.Optional(codec, _) => option(schemaDecoder(codec))
691681
case Schema.Tuple2(left, right, _) => ZJsonDecoder.tuple2(schemaDecoder(left), schemaDecoder(right))
692682
case Schema.Transform(c, f, _, a, _) => schemaDecoder(a.foldLeft(c)((s, a) => s.annotate(a)), discriminator).mapOrFail(f)
693683
case Schema.Sequence(codec, f, _, _, _) => ZJsonDecoder.chunk(schemaDecoder(codec)).map(f)
694684
case s @ Schema.NonEmptySequence(codec, _, _, _, _) => ZJsonDecoder.chunk(schemaDecoder(codec)).map(s.fromChunk)
695685
case Schema.Map(ks, vs, _) => mapDecoder(ks, vs)
696686
case Schema.NonEmptyMap(ks, vs, _) => mapDecoder(ks, vs).mapOrFail(m => NonEmptyMap.fromMapOption(m).toRight("NonEmptyMap expected"))
697-
case Schema.Set(s, _) => ZJsonDecoder.chunk(schemaDecoder(s)).map(entries => entries.toSet)
687+
case Schema.Set(s, _) => ZJsonDecoder.set(schemaDecoder(s))
698688
case Schema.Fail(message, _) => failDecoder(message)
699-
case s: Schema.GenericRecord => recordDecoder(s, discriminator)
700689
case Schema.Either(left, right, _) => ZJsonDecoder.either(schemaDecoder(left), schemaDecoder(right))
701690
case s @ Schema.Fallback(_, _, _, _) => fallbackDecoder(s)
702-
case l @ Schema.Lazy(_) => ZJsonDecoder.suspend(schemaDecoder(l.schema, discriminator))
691+
case l @ Schema.Lazy(_) => ZJsonDecoder.suspend(schemaDecoder(l.schema))
692+
case s: Schema.GenericRecord => recordDecoder(s, discriminator)
693+
case s: Schema.Enum[A] => enumDecoder(s)
703694
//case Schema.Meta(_, _) => astDecoder
704695
case s @ Schema.CaseClass0(_, _, _) => caseClass0Decoder(discriminator, s)
705696
case s @ Schema.CaseClass1(_, _, _, _) => caseClass1Decoder(discriminator, s)
@@ -738,7 +729,6 @@ object JsonCodec {
738729
caseClass21Decoder(discriminator, s)
739730
case s @ Schema.CaseClass22(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
740731
caseClass22Decoder(discriminator, s)
741-
case s: Schema.Enum[A] => enumDecoder(s)
742732
case d @ Schema.Dynamic(_) => dynamicDecoder(d)
743733
case _ => throw new Exception(s"Missing a handler for decoding of schema ${schema.toString()}.")
744734
}
@@ -751,9 +741,9 @@ object JsonCodec {
751741
val valueDecoder = JsonDecoder.schemaDecoder(vs)
752742
jsonFieldDecoder(ks) match {
753743
case Some(jsonFieldDecoder) =>
754-
ZJsonDecoder.keyValueChunk(jsonFieldDecoder, valueDecoder).map(_.toMap)
744+
ZJsonDecoder.map(jsonFieldDecoder, valueDecoder)
755745
case None =>
756-
ZJsonDecoder.chunk(schemaDecoder(ks).zip(schemaDecoder(vs))).map(_.toMap)
746+
ZJsonDecoder.chunk(schemaDecoder(ks).zip(valueDecoder)).map(_.toMap)
757747
}
758748
}
759749

0 commit comments

Comments
 (0)