aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/DerivedFormats.scala
diff options
context:
space:
mode:
authorJakob Odersky <jakob@odersky.com>2018-02-13 15:09:31 -0800
committerJakob Odersky <jakob@odersky.com>2018-02-13 15:35:12 -0800
commitaf6845cc08ae223ad67ebad61559d8264fd3346b (patch)
tree0983ef274c10eaacb0304db65e5217ec3fd00816 /src/main/scala/DerivedFormats.scala
downloadspray-json-derivation-af6845cc08ae223ad67ebad61559d8264fd3346b.tar.gz
spray-json-derivation-af6845cc08ae223ad67ebad61559d8264fd3346b.tar.bz2
spray-json-derivation-af6845cc08ae223ad67ebad61559d8264fd3346b.zip
Initial commit
Diffstat (limited to 'src/main/scala/DerivedFormats.scala')
-rw-r--r--src/main/scala/DerivedFormats.scala55
1 files changed, 55 insertions, 0 deletions
diff --git a/src/main/scala/DerivedFormats.scala b/src/main/scala/DerivedFormats.scala
new file mode 100644
index 0000000..6c2396e
--- /dev/null
+++ b/src/main/scala/DerivedFormats.scala
@@ -0,0 +1,55 @@
+import magnolia._
+import spray.json._
+
+import scala.language.experimental.macros
+
+trait JsonFormatDerivation extends DefaultJsonProtocol {
+ type Typeclass[T] = JsonFormat[T]
+
+ def combine[T](ctx: CaseClass[JsonFormat, T]): JsonFormat[T] = new JsonFormat[T] {
+ override def write(value: T): JsValue = {
+ val fields: Seq[(String, JsValue)] = ctx.parameters.map { param =>
+ param.label -> param.typeclass.write(param.dereference(value))
+ }
+ JsObject(fields: _*)
+ }
+ override def read(value: JsValue): T = value match {
+ case obj: JsObject =>
+ ctx.construct { param =>
+ param.typeclass.read(obj.fields(param.label))
+ }
+ case js =>
+ deserializationError(s"Cannot read JSON '$js' as a ${ctx.typeName}")
+ }
+ }
+
+ def dispatch[T](ctx: SealedTrait[JsonFormat, T]): JsonFormat[T] = new JsonFormat[T] {
+ override def write(value: T): JsValue = {
+ ctx.dispatch(value) { sub =>
+ val obj = sub.typeclass.write(sub.cast(value)).asJsObject
+ JsObject((obj.fields ++ Map("type" -> JsString(sub.label))).toSeq: _*)
+ }
+ }
+ override def read(value: JsValue): T = value match {
+ case obj: JsObject if obj.fields.contains("type") =>
+ val fieldName = obj.fields("type").convertTo[String]
+
+ ctx.subtypes.find(_.label == fieldName) match {
+ case Some(tpe) => tpe.typeclass.read(obj)
+ case None =>
+ deserializationError(
+ s"Cannot deserialize JSON to ${ctx.typeName} because type field '${fieldName}' has an unsupported value.")
+ }
+
+ case js =>
+ deserializationError(s"Cannot read JSON '$js' as a ${ctx.typeName}")
+ }
+
+ }
+
+ implicit def gen[T]: JsonFormat[T] = macro Magnolia.gen[T]
+
+}
+object JsonFormatDerivation extends JsonFormatDerivation
+
+trait DerivedFormats extends JsonFormatDerivation