aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jakob@odersky.com>2018-02-14 15:00:52 -0800
committerJakob Odersky <jakob@odersky.com>2018-02-14 15:00:52 -0800
commit7ab0889d7ab4480d0d90346432006ffd345b4916 (patch)
treefccce4902dfc4241a651dfac32e4ef77288de3c9
parent5a37bfee89a2b5ae17c7980327a92c381c006cf7 (diff)
downloadspray-json-derivation-7ab0889d7ab4480d0d90346432006ffd345b4916.tar.gz
spray-json-derivation-7ab0889d7ab4480d0d90346432006ffd345b4916.tar.bz2
spray-json-derivation-7ab0889d7ab4480d0d90346432006ffd345b4916.zip
Remove need for special case for enums
-rw-r--r--README.md14
-rw-r--r--src/main/scala/DerivedFormats.scala86
-rw-r--r--src/main/scala/annotations.scala9
-rw-r--r--src/test/scala/CoproductTypeFormats.scala9
4 files changed, 49 insertions, 69 deletions
diff --git a/README.md b/README.md
index 9cc0cce..de5e4fd 100644
--- a/README.md
+++ b/README.md
@@ -5,9 +5,17 @@
# Spray JSON Format Derivation
-This library provides automatic spray JsonFormats for any `case class` and children of `sealed trait`s.
-
-It uses the [Magnolia](http://magnolia.work/) ([source code](https://github.com/propensive/magnolia)) type-derivation library to implicitly generate JSON formats for any product type. Magnolia integrates with spray so seamlessly that it is almost not worth the effort to publish this project as a full fledged repository; a single gist with the contents of [DerivedFormats.scala](src/main/scala/DerivedFormats.scala) would demonstrate almost all functionality.
+This library provides automatic spray JsonFormats for any `case class`
+and children of `sealed trait`s.
+
+It uses the [Magnolia](http://magnolia.work/) ([source
+code](https://github.com/propensive/magnolia)) type-derivation library
+to implicitly generate JSON formats for any product type. Magnolia
+integrates with spray so seamlessly that it is almost not worth the
+effort to publish this project as a full fledged repository; a single
+gist with the contents of
+[DerivedFormats.scala](src/main/scala/DerivedFormats.scala) would
+demonstrate almost all functionality.
## Getting Started
diff --git a/src/main/scala/DerivedFormats.scala b/src/main/scala/DerivedFormats.scala
index 08c452c..4fc096e 100644
--- a/src/main/scala/DerivedFormats.scala
+++ b/src/main/scala/DerivedFormats.scala
@@ -27,7 +27,7 @@ trait DerivedFormats { self: BasicFormats =>
ctx.construct { param =>
param.typeclass.read(obj.fields(param.label))
}
- case str: JsString if ctx.isObject && str.value == ctx.typeName.short =>
+ case JsString(str) if ctx.isObject && str == ctx.typeName.short =>
ctx.rawConstruct(Seq.empty)
case js =>
@@ -36,64 +36,46 @@ trait DerivedFormats { self: BasicFormats =>
}
}
- def dispatch[T](ctx: SealedTrait[JsonFormat, T]): JsonFormat[T] =
- new JsonFormat[T] {
- def tpe =
- ctx.annotations
- .find(_.isInstanceOf[JsonAnnotation])
- .getOrElse(new gadt("type"))
-
- override def write(value: T): JsValue = tpe match {
- case _: enum =>
- ctx.dispatch(value) { sub =>
- JsString(sub.typeName.short)
- }
+ def dispatch[T](ctx: SealedTrait[JsonFormat, T]): JsonFormat[T] = {
+ val typeFieldName = ctx.annotations
+ .collectFirst{
+ case g: gadt => g.typeFieldName
+ }
+ .getOrElse("type")
- case g: gadt =>
- ctx.dispatch(value) { sub =>
- val obj = sub.typeclass.write(sub.cast(value)).asJsObject
+ new JsonFormat[T] {
+ override def write(value: T): JsValue = ctx.dispatch(value) { sub =>
+ sub.typeclass.write(sub.cast(value)) match {
+ case obj: JsObject =>
JsObject(
- (Map(g.typeFieldName -> JsString(sub.typeName.short)) ++
+ (Map(typeFieldName -> JsString(sub.typeName.short)) ++
obj.fields).toSeq: _*)
- }
+ case js => js
+ }
}
- override def read(value: JsValue): T = tpe match {
- case _: enum =>
- value match {
- case str: JsString =>
- ctx.subtypes
- .find(_.typeName.short == str.value)
- .getOrElse(deserializationError(
- s"Cannot deserialize JSON to ${ctx.typeName.full} because " +
- "type '${str}' has an unsupported value."))
- .typeclass
- .read(str)
- case js =>
- deserializationError(
- s"Cannot read JSON '$js' as a ${ctx.typeName.full}")
- }
-
- case g: gadt =>
- value match {
- case obj: JsObject if obj.fields.contains(g.typeFieldName) =>
- val fieldName = obj.fields(g.typeFieldName).convertTo[String]
-
- ctx.subtypes.find(_.typeName.short == fieldName) match {
- case Some(tpe) => tpe.typeclass.read(obj)
- case None =>
- deserializationError(
- s"Cannot deserialize JSON to ${ctx.typeName.full} " +
- s"because type field '${fieldName}' has an unsupported " +
- "value.")
- }
+ override def read(js: JsValue): T = {
+ val typeName: String = js match {
+ case obj: JsObject if obj.fields.contains(typeFieldName) =>
+ obj.fields(typeFieldName).convertTo[String]
+ case JsString(str) =>
+ str
+ case _ =>
+ deserializationError(
+ s"Cannot deserialize JSON to ${ctx.typeName.full} " +
+ "because serialized type cannot be determined.")
+ }
- case js =>
- deserializationError(
- s"Cannot read JSON '$js' as a ${ctx.typeName}")
- }
+ ctx.subtypes.find(_.typeName.short == typeName) match {
+ case Some(tpe) => tpe.typeclass.read(js)
+ case None =>
+ deserializationError(
+ s"Cannot deserialize JSON to ${ctx.typeName.full} " +
+ s"because type '${typeName}' is unsupported.")
+ }
}
- }
+ }
+ }
implicit def gen[T]: JsonFormat[T] = macro Magnolia.gen[T]
diff --git a/src/main/scala/annotations.scala b/src/main/scala/annotations.scala
index f23fbcb..ee179ff 100644
--- a/src/main/scala/annotations.scala
+++ b/src/main/scala/annotations.scala
@@ -2,9 +2,6 @@ package xyz.driver.json
import scala.annotation.StaticAnnotation
-/** Indicator trait of anontations related to JSON formatting. */
-sealed trait JsonAnnotation
-
/** An annotation that designates that a sealed trait is a generalized algebraic
* datatype (GADT), and that a type field containing the serialized childrens'
* types should be added to the final JSON objects.
@@ -25,9 +22,3 @@ sealed trait JsonAnnotation
* object */
final class gadt(val typeFieldName: String = "type")
extends StaticAnnotation
- with JsonAnnotation
-
-/** An annotation that designates that a sealed trait is an enumeration (all
- * children are strictly case objects), and that all children should be
- * serialized as strings. */
-final class enum extends StaticAnnotation with JsonAnnotation
diff --git a/src/test/scala/CoproductTypeFormats.scala b/src/test/scala/CoproductTypeFormats.scala
index f16e4c7..f9b71b8 100644
--- a/src/test/scala/CoproductTypeFormats.scala
+++ b/src/test/scala/CoproductTypeFormats.scala
@@ -31,10 +31,10 @@ class CoproductTypeFormats
"""{"type":"Plus","lhs":{"type":"Value","x":42},"rhs":{"type":"Value","x":0}}"""
)
- // "Case object child" should behave like checkCoherence[Expr](
- // One,
- // """{"type":"One"}"""
- // )
+ "Case object child" should behave like checkCoherence[Expr](
+ One,
+ """"One""""
+ )
@gadt("kind")
sealed abstract class Keyword(`type`: String)
@@ -45,7 +45,6 @@ class CoproductTypeFormats
"""{"kind":"If","type":"class"}"""
)
- @enum
sealed trait Enum
case object A extends Enum
case object B extends Enum