Skip to content

Commit

Permalink
Add Schema for zio.json.ast.Json (#599)
Browse files Browse the repository at this point in the history
  • Loading branch information
987Nabil authored Aug 30, 2023
1 parent 620d57d commit 1861252
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package zio.schema.codec

import java.util.Base64

import scala.collection.immutable.ListMap

import zio.Chunk
import zio.json.ast.Json
import zio.schema.annotation.directDynamicMapping
import zio.schema.{ DynamicValue, Schema, StandardType, TypeId }

package object json {
implicit val schemaJson: Schema[Json] =
Schema.dynamicValue.annotate(directDynamicMapping()).transform(toJson, fromJson)

private def toJson(dv: DynamicValue): Json =
dv match {
case DynamicValue.Record(_, values) =>
values.foldLeft(Json.Obj()) { case (obj, (name, value)) => (name, toJson(value)) +: obj }
case DynamicValue.Enumeration(_, _) =>
throw new Exception("DynamicValue.Enumeration is unsupported")
case DynamicValue.Sequence(values) =>
Json.Arr(values.map(toJson))
case DynamicValue.Dictionary(_) =>
throw new Exception("DynamicValue.Dictionary is unsupported")
case DynamicValue.SetValue(values) =>
Json.Arr(Chunk.fromIterable(values.map(toJson)))
case DynamicValue.Primitive(value, standardType) =>
standardType.asInstanceOf[StandardType[_]] match {
case StandardType.UnitType => Json.Obj()
case StandardType.StringType => Json.Str(value.asInstanceOf[String])
case StandardType.BoolType => Json.Bool(value.asInstanceOf[Boolean])
case StandardType.ByteType => Json.Num(value.asInstanceOf[Byte])
case StandardType.ShortType => Json.Num(value.asInstanceOf[Short])
case StandardType.IntType => Json.Num(value.asInstanceOf[Int])
case StandardType.LongType => Json.Num(value.asInstanceOf[Long])
case StandardType.FloatType => Json.Num(value.asInstanceOf[Float])
case StandardType.DoubleType => Json.Num(value.asInstanceOf[Double])
case StandardType.BinaryType =>
Json.Str(Base64.getEncoder.encodeToString(value.asInstanceOf[Chunk[Byte]].toArray))
case StandardType.CharType => Json.Str(value.asInstanceOf[Char].toString)
case StandardType.UUIDType => Json.Str(value.asInstanceOf[java.util.UUID].toString)
case StandardType.BigDecimalType => Json.Num(value.asInstanceOf[java.math.BigDecimal])
case StandardType.BigIntegerType => Json.Num(BigDecimal(value.asInstanceOf[java.math.BigInteger]))
case StandardType.DayOfWeekType => Json.Str(value.asInstanceOf[java.time.DayOfWeek].toString)
case StandardType.MonthType => Json.Str(value.asInstanceOf[java.time.Month].toString)
case StandardType.MonthDayType => Json.Str(value.asInstanceOf[java.time.MonthDay].toString)
case StandardType.PeriodType => Json.Str(value.asInstanceOf[java.time.Period].toString)
case StandardType.YearType => Json.Num(value.asInstanceOf[java.time.Year].getValue)
case StandardType.YearMonthType => Json.Str(value.asInstanceOf[java.time.YearMonth].toString)
case StandardType.ZoneIdType => Json.Str(value.asInstanceOf[java.time.ZoneId].toString)
case StandardType.ZoneOffsetType => Json.Str(value.asInstanceOf[java.time.ZoneOffset].toString)
case StandardType.DurationType => Json.Str(value.asInstanceOf[java.time.Duration].toString)
case StandardType.InstantType => Json.Str(value.asInstanceOf[java.time.Instant].toString)
case StandardType.LocalDateType => Json.Str(value.asInstanceOf[java.time.LocalDate].toString)
case StandardType.LocalTimeType => Json.Str(value.asInstanceOf[java.time.LocalTime].toString)
case StandardType.LocalDateTimeType => Json.Str(value.asInstanceOf[java.time.LocalDateTime].toString)
case StandardType.OffsetTimeType => Json.Str(value.asInstanceOf[java.time.OffsetTime].toString)
case StandardType.OffsetDateTimeType => Json.Str(value.asInstanceOf[java.time.OffsetDateTime].toString)
case StandardType.ZonedDateTimeType => Json.Str(value.asInstanceOf[java.time.ZonedDateTime].toString)
}
case DynamicValue.Singleton(_) => Json.Obj()
case DynamicValue.SomeValue(value) => toJson(value)
case DynamicValue.NoneValue => Json.Null
case DynamicValue.Tuple(left, right) => Json.Arr(Chunk(toJson(left), toJson(right)))
case DynamicValue.LeftValue(value) => Json.Obj("Left" -> toJson(value))
case DynamicValue.RightValue(value) => Json.Obj("Right" -> toJson(value))
case DynamicValue.DynamicAst(_) => throw new Exception("DynamicValue.DynamicAst is unsupported")
case DynamicValue.Error(_) => throw new Exception("DynamicValue.Error is unsupported")
}

private def fromJson(json: Json): DynamicValue =
json match {
case Json.Null => DynamicValue.NoneValue
case Json.Bool(value) => DynamicValue.Primitive(value, StandardType.BoolType)
case Json.Num(value) => DynamicValue.Primitive(value, StandardType.BigDecimalType)
case Json.Str(value) => DynamicValue.Primitive(value, StandardType.StringType)
case Json.Arr(values) => DynamicValue.Sequence(values.map(fromJson))
case Json.Obj(values) =>
DynamicValue.Record(
TypeId.parse("Json.Obj"),
ListMap(values.map { case (name, value) => (name, fromJson(value)) }.toList: _*)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import scala.collection.immutable.ListMap
import zio.Console._
import zio._
import zio.json.JsonDecoder.JsonError
import zio.json.ast.Json
import zio.json.{ DeriveJsonEncoder, JsonEncoder }
import zio.schema.CaseSet._
import zio.schema._
Expand Down Expand Up @@ -251,6 +252,64 @@ object JsonCodecSpec extends ZIOSpecDefault {
charSequenceToByteChunk("""{"foo":"s","bar":1}""")
)
}
),
suite("zio.json.ast.Json encoding")(
test("Json.Obj") {
assertEncodes(
zio.schema.codec.json.schemaJson,
Json.Obj("foo" -> Json.Str("bar"), "null" -> Json.Null),
charSequenceToByteChunk("""{"foo":"bar","null":null}""")
)
},
test("Json.Arr") {
assertEncodes(
zio.schema.codec.json.schemaJson,
Json.Arr(Json.Str("foo"), Json.Num(1)),
charSequenceToByteChunk("""["foo",1]""")
)
},
test("Json.Num Int") {
assertEncodes(
zio.schema.codec.json.schemaJson,
Json.Num(1),
charSequenceToByteChunk("""1""")
)
},
test("Json.Num Long") {
assertEncodes(
zio.schema.codec.json.schemaJson,
Json.Num(1L),
charSequenceToByteChunk("""1""")
)
},
test("Json.Num Double") {
assertEncodes(
zio.schema.codec.json.schemaJson,
Json.Num(1.1),
charSequenceToByteChunk("""1.1""")
)
},
test("Json.Str") {
assertEncodes(
zio.schema.codec.json.schemaJson,
Json.Str("foo"),
charSequenceToByteChunk(""""foo"""")
)
},
test("Json.Bool") {
assertEncodes(
zio.schema.codec.json.schemaJson,
Json.Bool(true),
charSequenceToByteChunk("""true""")
)
},
test("Json.Null") {
assertEncodes(
zio.schema.codec.json.schemaJson,
Json.Null,
charSequenceToByteChunk("""null""")
)
}
)
)

Expand Down Expand Up @@ -488,6 +547,64 @@ object JsonCodecSpec extends ZIOSpecDefault {
)
)
}
),
suite("zio.json.ast.Json decoding")(
test("Json.Obj") {
assertDecodes(
zio.schema.codec.json.schemaJson,
Json.Obj("foo" -> Json.Str("bar"), "null" -> Json.Null),
charSequenceToByteChunk("""{"foo":"bar","null":null}""")
)
},
test("Json.Arr") {
assertDecodes(
zio.schema.codec.json.schemaJson,
Json.Arr(Json.Str("foo"), Json.Num(1)),
charSequenceToByteChunk("""["foo",1]""")
)
},
test("Json.Num Int") {
assertDecodes(
zio.schema.codec.json.schemaJson,
Json.Num(1),
charSequenceToByteChunk("""1""")
)
},
test("Json.Num Long") {
assertDecodes(
zio.schema.codec.json.schemaJson,
Json.Num(1L),
charSequenceToByteChunk("""1""")
)
},
test("Json.Num Double") {
assertDecodes(
zio.schema.codec.json.schemaJson,
Json.Num(1.1),
charSequenceToByteChunk("""1.1""")
)
},
test("Json.Str") {
assertDecodes(
zio.schema.codec.json.schemaJson,
Json.Str("foo"),
charSequenceToByteChunk(""""foo"""")
)
},
test("Json.Bool") {
assertDecodes(
zio.schema.codec.json.schemaJson,
Json.Bool(true),
charSequenceToByteChunk("""true""")
)
},
test("Json.Null") {
assertDecodes(
zio.schema.codec.json.schemaJson,
Json.Null,
charSequenceToByteChunk("""null""")
)
}
)
)

Expand Down

0 comments on commit 1861252

Please sign in to comment.