summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/scala/cc/spray/json/ProductFormats.scala516
-rw-r--r--src/main/scala/cc/spray/json/StandardFormats.scala8
-rw-r--r--src/test/scala/cc/spray/json/ProductFormatsSpec.scala18
3 files changed, 283 insertions, 259 deletions
diff --git a/src/main/scala/cc/spray/json/ProductFormats.scala b/src/main/scala/cc/spray/json/ProductFormats.scala
index 39b44fb..ca524bb 100644
--- a/src/main/scala/cc/spray/json/ProductFormats.scala
+++ b/src/main/scala/cc/spray/json/ProductFormats.scala
@@ -17,121 +17,122 @@
package cc.spray.json
+import annotation.tailrec
+
/**
* Provides the helpers for constructing custom JsonFormat implementations for types implementing the Product trait
* (especially case classes)
*/
trait ProductFormats {
-
- private type JF[T] = JsonFormat[T] // simple alias for reduced verbosity
+ this: StandardFormats =>
def jsonFormat[A :JF, T <: Product](construct: A => T, a: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson)
+ toField[A](a, p, 0)
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A]
+ fromField[A](value, a)
)
}
def jsonFormat[A :JF, B :JF, T <: Product](construct: (A, B) => T, a: String, b: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B]
+ fromField[A](value, a),
+ fromField[B](value, b)
)
}
def jsonFormat[A :JF, B :JF, C :JF, T <: Product](construct: (A, B, C) => T,
a: String, b: String, c: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2)))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c)
)
}
def jsonFormat[A :JF, B :JF, C :JF, D :JF, T <: Product](construct: (A, B, C, D) => T,
a: String, b: String, c: String, d: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d)
)
}
def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, T <: Product](construct: (A, B, C, D, E) => T,
a: String, b: String, c: String, d: String, e: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4)))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e)
)
}
def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, T <: Product](construct: (A, B, C, D, E, F) => T,
a: String, b: String, c: String, d: String, e: String, f: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson),
- JsField(f, element[F](p, 5).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4,
+ toField[F](f, p, 5))))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E],
- field(value, f).fromJson[F]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e),
+ fromField[F](value, f)
)
}
def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, T <: Product](construct: (A, B, C, D, E, F, G) => T,
a: String, b: String, c: String, d: String, e: String, f: String, g: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson),
- JsField(f, element[F](p, 5).toJson),
- JsField(g, element[G](p, 6).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4,
+ toField[F](f, p, 5,
+ toField[G](g, p, 6)))))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E],
- field(value, f).fromJson[F],
- field(value, g).fromJson[G]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e),
+ fromField[F](value, f),
+ fromField[G](value, g)
)
}
@@ -139,24 +140,24 @@ trait ProductFormats {
(construct: (A, B, C, D, E, F, G, H) => T,
a: String, b: String, c: String, d: String, e: String, f: String, g: String, h: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson),
- JsField(f, element[F](p, 5).toJson),
- JsField(g, element[G](p, 6).toJson),
- JsField(h, element[H](p, 7).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4,
+ toField[F](f, p, 5,
+ toField[G](g, p, 6,
+ toField[H](h, p, 7))))))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E],
- field(value, f).fromJson[F],
- field(value, g).fromJson[G],
- field(value, h).fromJson[H]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e),
+ fromField[F](value, f),
+ fromField[G](value, g),
+ fromField[H](value, h)
)
}
@@ -164,26 +165,26 @@ trait ProductFormats {
(construct: (A, B, C, D, E, F, G, H, I) => T,
a: String, b: String, c: String, d: String, e: String, f: String, g: String, h: String, i: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson),
- JsField(f, element[F](p, 5).toJson),
- JsField(g, element[G](p, 6).toJson),
- JsField(h, element[H](p, 7).toJson),
- JsField(i, element[I](p, 8).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4,
+ toField[F](f, p, 5,
+ toField[G](g, p, 6,
+ toField[H](h, p, 7,
+ toField[I](i, p, 8)))))))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E],
- field(value, f).fromJson[F],
- field(value, g).fromJson[G],
- field(value, h).fromJson[H],
- field(value, i).fromJson[I]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e),
+ fromField[F](value, f),
+ fromField[G](value, g),
+ fromField[H](value, h),
+ fromField[I](value, i)
)
}
@@ -191,28 +192,28 @@ trait ProductFormats {
(construct: (A, B, C, D, E, F, G, H, I, J) => T, a: String, b: String, c: String, d: String, e: String,
f: String, g: String, h: String, i: String, j: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson),
- JsField(f, element[F](p, 5).toJson),
- JsField(g, element[G](p, 6).toJson),
- JsField(h, element[H](p, 7).toJson),
- JsField(i, element[I](p, 8).toJson),
- JsField(j, element[J](p, 9).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4,
+ toField[F](f, p, 5,
+ toField[G](g, p, 6,
+ toField[H](h, p, 7,
+ toField[I](i, p, 8,
+ toField[J](j, p, 9))))))))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E],
- field(value, f).fromJson[F],
- field(value, g).fromJson[G],
- field(value, h).fromJson[H],
- field(value, i).fromJson[I],
- field(value, j).fromJson[J]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e),
+ fromField[F](value, f),
+ fromField[G](value, g),
+ fromField[H](value, h),
+ fromField[I](value, i),
+ fromField[J](value, j)
)
}
@@ -220,30 +221,30 @@ trait ProductFormats {
(construct: (A, B, C, D, E, F, G, H, I, J, K) => T, a: String, b: String, c: String, d: String, e: String,
f: String, g: String, h: String, i: String, j: String, k: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson),
- JsField(f, element[F](p, 5).toJson),
- JsField(g, element[G](p, 6).toJson),
- JsField(h, element[H](p, 7).toJson),
- JsField(i, element[I](p, 8).toJson),
- JsField(j, element[J](p, 9).toJson),
- JsField(k, element[K](p, 10).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4,
+ toField[F](f, p, 5,
+ toField[G](g, p, 6,
+ toField[H](h, p, 7,
+ toField[I](i, p, 8,
+ toField[J](j, p, 9,
+ toField[K](k, p, 10)))))))))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E],
- field(value, f).fromJson[F],
- field(value, g).fromJson[G],
- field(value, h).fromJson[H],
- field(value, i).fromJson[I],
- field(value, j).fromJson[J],
- field(value, k).fromJson[K]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e),
+ fromField[F](value, f),
+ fromField[G](value, g),
+ fromField[H](value, h),
+ fromField[I](value, i),
+ fromField[J](value, j),
+ fromField[K](value, k)
)
}
@@ -251,32 +252,32 @@ trait ProductFormats {
(construct: (A, B, C, D, E, F, G, H, I, J, K, L) => T, a: String, b: String, c: String, d: String, e: String,
f: String, g: String, h: String, i: String, j: String, k: String, l: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson),
- JsField(f, element[F](p, 5).toJson),
- JsField(g, element[G](p, 6).toJson),
- JsField(h, element[H](p, 7).toJson),
- JsField(i, element[I](p, 8).toJson),
- JsField(j, element[J](p, 9).toJson),
- JsField(k, element[K](p, 10).toJson),
- JsField(l, element[L](p, 11).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4,
+ toField[F](f, p, 5,
+ toField[G](g, p, 6,
+ toField[H](h, p, 7,
+ toField[I](i, p, 8,
+ toField[J](j, p, 9,
+ toField[K](k, p, 10,
+ toField[L](l, p, 11))))))))))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E],
- field(value, f).fromJson[F],
- field(value, g).fromJson[G],
- field(value, h).fromJson[H],
- field(value, i).fromJson[I],
- field(value, j).fromJson[J],
- field(value, k).fromJson[K],
- field(value, l).fromJson[L]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e),
+ fromField[F](value, f),
+ fromField[G](value, g),
+ fromField[H](value, h),
+ fromField[I](value, i),
+ fromField[J](value, j),
+ fromField[K](value, k),
+ fromField[L](value, l)
)
}
@@ -284,34 +285,34 @@ trait ProductFormats {
(construct: (A, B, C, D, E, F, G, H, I, J, K, L, M) => T, a: String, b: String, c: String, d: String, e: String,
f: String, g: String, h: String, i: String, j: String, k: String, l: String, m: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson),
- JsField(f, element[F](p, 5).toJson),
- JsField(g, element[G](p, 6).toJson),
- JsField(h, element[H](p, 7).toJson),
- JsField(i, element[I](p, 8).toJson),
- JsField(j, element[J](p, 9).toJson),
- JsField(k, element[K](p, 10).toJson),
- JsField(l, element[L](p, 11).toJson),
- JsField(m, element[M](p, 12).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4,
+ toField[F](f, p, 5,
+ toField[G](g, p, 6,
+ toField[H](h, p, 7,
+ toField[I](i, p, 8,
+ toField[J](j, p, 9,
+ toField[K](k, p, 10,
+ toField[L](l, p, 11,
+ toField[M](m, p, 12)))))))))))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E],
- field(value, f).fromJson[F],
- field(value, g).fromJson[G],
- field(value, h).fromJson[H],
- field(value, i).fromJson[I],
- field(value, j).fromJson[J],
- field(value, k).fromJson[K],
- field(value, l).fromJson[L],
- field(value, m).fromJson[M]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e),
+ fromField[F](value, f),
+ fromField[G](value, g),
+ fromField[H](value, h),
+ fromField[I](value, i),
+ fromField[J](value, j),
+ fromField[K](value, k),
+ fromField[L](value, l),
+ fromField[M](value, m)
)
}
@@ -319,36 +320,36 @@ trait ProductFormats {
(construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N) => T, a: String, b: String, c: String, d: String, e: String,
f: String, g: String, h: String, i: String, j: String, k: String, l: String, m: String, n: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson),
- JsField(f, element[F](p, 5).toJson),
- JsField(g, element[G](p, 6).toJson),
- JsField(h, element[H](p, 7).toJson),
- JsField(i, element[I](p, 8).toJson),
- JsField(j, element[J](p, 9).toJson),
- JsField(k, element[K](p, 10).toJson),
- JsField(l, element[L](p, 11).toJson),
- JsField(m, element[M](p, 12).toJson),
- JsField(n, element[N](p, 13).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4,
+ toField[F](f, p, 5,
+ toField[G](g, p, 6,
+ toField[H](h, p, 7,
+ toField[I](i, p, 8,
+ toField[J](j, p, 9,
+ toField[K](k, p, 10,
+ toField[L](l, p, 11,
+ toField[M](m, p, 12,
+ toField[N](n, p, 13))))))))))))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E],
- field(value, f).fromJson[F],
- field(value, g).fromJson[G],
- field(value, h).fromJson[H],
- field(value, i).fromJson[I],
- field(value, j).fromJson[J],
- field(value, k).fromJson[K],
- field(value, l).fromJson[L],
- field(value, m).fromJson[M],
- field(value, n).fromJson[N]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e),
+ fromField[F](value, f),
+ fromField[G](value, g),
+ fromField[H](value, h),
+ fromField[I](value, i),
+ fromField[J](value, j),
+ fromField[K](value, k),
+ fromField[L](value, l),
+ fromField[M](value, m),
+ fromField[N](value, n)
)
}
@@ -356,52 +357,67 @@ trait ProductFormats {
(construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => T, a: String, b: String, c: String, d: String, e: String,
f: String, g: String, h: String, i: String, j: String, k: String, l: String, m: String, n: String, o: String) = new JF[T]{
def write(p: T) = JsObject(
- JsField(a, element[A](p, 0).toJson),
- JsField(b, element[B](p, 1).toJson),
- JsField(c, element[C](p, 2).toJson),
- JsField(d, element[D](p, 3).toJson),
- JsField(e, element[E](p, 4).toJson),
- JsField(f, element[F](p, 5).toJson),
- JsField(g, element[G](p, 6).toJson),
- JsField(h, element[H](p, 7).toJson),
- JsField(i, element[I](p, 8).toJson),
- JsField(j, element[J](p, 9).toJson),
- JsField(k, element[K](p, 10).toJson),
- JsField(l, element[L](p, 11).toJson),
- JsField(m, element[M](p, 12).toJson),
- JsField(n, element[N](p, 13).toJson),
- JsField(o, element[O](p, 14).toJson)
+ toField[A](a, p, 0,
+ toField[B](b, p, 1,
+ toField[C](c, p, 2,
+ toField[D](d, p, 3,
+ toField[E](e, p, 4,
+ toField[F](f, p, 5,
+ toField[G](g, p, 6,
+ toField[H](h, p, 7,
+ toField[I](i, p, 8,
+ toField[J](j, p, 9,
+ toField[K](k, p, 10,
+ toField[L](l, p, 11,
+ toField[M](m, p, 12,
+ toField[N](n, p, 13,
+ toField[O](o, p, 14)))))))))))))))
)
def read(value: JsValue) = construct(
- field(value, a).fromJson[A],
- field(value, b).fromJson[B],
- field(value, c).fromJson[C],
- field(value, d).fromJson[D],
- field(value, e).fromJson[E],
- field(value, f).fromJson[F],
- field(value, g).fromJson[G],
- field(value, h).fromJson[H],
- field(value, i).fromJson[I],
- field(value, j).fromJson[J],
- field(value, k).fromJson[K],
- field(value, l).fromJson[L],
- field(value, m).fromJson[M],
- field(value, n).fromJson[N],
- field(value, o).fromJson[O]
+ fromField[A](value, a),
+ fromField[B](value, b),
+ fromField[C](value, c),
+ fromField[D](value, d),
+ fromField[E](value, e),
+ fromField[F](value, f),
+ fromField[G](value, g),
+ fromField[H](value, h),
+ fromField[I](value, i),
+ fromField[J](value, j),
+ fromField[K](value, k),
+ fromField[L](value, l),
+ fromField[M](value, m),
+ fromField[N](value, n),
+ fromField[O](value, o)
)
}
// helpers
- private def element[T](p: Product, ix: Int) = p.productElement(ix).asInstanceOf[T]
+ private def toField[T](fieldName: String, p: Product, ix: Int, rest: List[JsField] = Nil)
+ (implicit writer: JsonWriter[T]): List[JsField] = {
+ val value = p.productElement(ix).asInstanceOf[T]
+ writer match {
+ case _: OptionFormat[_] if (value == None) => rest
+ case _ => JsField(fieldName, writer.write(value)) :: rest
+ }
+ }
- private def field(value: JsValue, fieldName: String) = value match {
- case jso: JsObject => {
- jso.fields
- .find(_.name == fieldName)
- .getOrElse(throw new DeserializationException("Object is missing required member '" + fieldName + "'"))
- .value
+ private def fromField[T](value: JsValue, fieldName: String)(implicit reader: JsonReader[T]) = {
+ @tailrec
+ def getFrom(fields: List[JsField]): T = {
+ if (fields.isEmpty) {
+ if (reader.isInstanceOf[OptionFormat[_]]) None.asInstanceOf[T]
+ else throw new DeserializationException("Object is missing required member '" + fieldName + "'")
+ } else if (fields.head.name == fieldName) {
+ reader.read(fields.head.value)
+ } else {
+ getFrom(fields.tail)
+ }
+ }
+ value match {
+ case x: JsObject => getFrom(x.fields)
+ case _ => throw new DeserializationException("Object expected")
}
- case _ => throw new DeserializationException("Object expected")
}
}
diff --git a/src/main/scala/cc/spray/json/StandardFormats.scala b/src/main/scala/cc/spray/json/StandardFormats.scala
index 8b0dc3a..1c44716 100644
--- a/src/main/scala/cc/spray/json/StandardFormats.scala
+++ b/src/main/scala/cc/spray/json/StandardFormats.scala
@@ -25,16 +25,18 @@ import scala.{Left, Right}
trait StandardFormats {
this: AdditionalFormats =>
- private type JF[T] = JsonFormat[T] // simple alias for reduced verbosity
+ private[json] type JF[T] = JsonFormat[T] // simple alias for reduced verbosity
- implicit def optionFormat[T :JF] = new JF[Option[T]] {
+ implicit def optionFormat[T :JF] = new OptionFormat[T]
+
+ class OptionFormat[T :JF] extends JF[Option[T]] {
def write(option: Option[T]) = option match {
case Some(x) => x.toJson
case None => JsNull
}
def read(value: JsValue) = value match {
case JsNull => None
- case x => Some(x.fromJson)
+ case x => Some(x.fromJson[T])
}
}
diff --git a/src/test/scala/cc/spray/json/ProductFormatsSpec.scala b/src/test/scala/cc/spray/json/ProductFormatsSpec.scala
index 763b4b0..221d8ae 100644
--- a/src/test/scala/cc/spray/json/ProductFormatsSpec.scala
+++ b/src/test/scala/cc/spray/json/ProductFormatsSpec.scala
@@ -4,25 +4,31 @@ import org.specs2.mutable._
class ProductFormatsSpec extends Specification with DefaultJsonProtocol {
- case class Test2(a: Int, b: Double)
+ case class Test2(a: Int, b: Option[Double])
implicit val test2Format = jsonFormat(Test2, "a", "b")
case class Test3[A, B](as: List[A], bs: List[B])
implicit def test3Format[A: JsonFormat, B: JsonFormat] = jsonFormat(Test3.apply[A, B], "as", "bs")
"A JsonFormat created with `jsonFormat`, for a case class with 2 elements," should {
- val obj = Test2(42, 4.2)
+ val obj = Test2(42, Some(4.2))
val json = JsObject(JsField("a", 42), JsField("b", 4.2))
"convert to a respective JsObject" in {
obj.toJson mustEqual json
}
"convert a JsObject to the respective case class instance" in {
- json.fromJson[Test2] mustEqual obj
+ json.fromJson[Test2] mustEqual obj
}
- "throw a DeserializationException if the JsObject does not define the right members" in (
- JsObject(JsField("a", 42), JsField("x", 4.2)).fromJson[Test2] must
- throwA(new DeserializationException("Object is missing required member 'b'"))
+ "throw a DeserializationException if the JsObject does not all required members" in (
+ JsObject(JsField("b", 4.2)).fromJson[Test2] must
+ throwA(new DeserializationException("Object is missing required member 'a'"))
)
+ "not require the presence of optional fields for deserialization" in {
+ JsObject(JsField("a", 42)).fromJson[Test2] mustEqual Test2(42, None)
+ }
+ "not render `None` members during serialization" in {
+ Test2(42, None).toJson mustEqual JsObject(JsField("a", 42))
+ }
"ignore additional members during deserialization" in {
JsObject(JsField("a", 42), JsField("b", 4.2), JsField("c", 'no)).fromJson[Test2] mustEqual obj
}