diff options
author | Mathias <mathias@spray.cc> | 2011-09-28 15:45:44 +0200 |
---|---|---|
committer | Mathias <mathias@spray.cc> | 2011-09-28 15:45:44 +0200 |
commit | 6945136f15ca6f6378e79d5ca2708ddf0041102a (patch) | |
tree | afd1566aa28a271d7c979dca7497e0ceb520f493 /src | |
parent | 48b6d35942963b412810b3cc61d3c3ca7f79f513 (diff) | |
download | spray-json-6945136f15ca6f6378e79d5ca2708ddf0041102a.tar.gz spray-json-6945136f15ca6f6378e79d5ca2708ddf0041102a.tar.bz2 spray-json-6945136f15ca6f6378e79d5ca2708ddf0041102a.zip |
Improve Option handling, speed up case class (de)serialization, closes #5
Diffstat (limited to 'src')
-rw-r--r-- | src/main/scala/cc/spray/json/ProductFormats.scala | 516 | ||||
-rw-r--r-- | src/main/scala/cc/spray/json/StandardFormats.scala | 8 | ||||
-rw-r--r-- | src/test/scala/cc/spray/json/ProductFormatsSpec.scala | 18 |
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 } |