From f6fa5236a430f0b935353794076267a8906859b4 Mon Sep 17 00:00:00 2001 From: Ruud Diterwich Date: Sun, 19 May 2013 14:04:01 +0200 Subject: [PATCH 1/5] Support for default field values. --- .../scala/spray/json/ProductFormats.scala | 350 ++++++++++-------- .../scala/spray/json/ProductFormatsSpec.scala | 16 + 2 files changed, 208 insertions(+), 158 deletions(-) diff --git a/src/main/scala/spray/json/ProductFormats.scala b/src/main/scala/spray/json/ProductFormats.scala index 1f5f7cf9..8436e068 100644 --- a/src/main/scala/spray/json/ProductFormats.scala +++ b/src/main/scala/spray/json/ProductFormats.scala @@ -25,6 +25,10 @@ import java.lang.reflect.Modifier trait ProductFormats { this: StandardFormats => + type FieldAndDefault = (String, Option[() => Any]) + implicit def toFieldAndDefArg(s : String) = (s, None) + implicit def toFieldAndDefArg(s : (String, () => Any)) = (s._1, Some(s._2)) + def jsonFormat0[T <: Product :ClassManifest](construct: () => T): RootJsonFormat[T] = { jsonFormat(construct) } @@ -37,9 +41,9 @@ trait ProductFormats { val Array(a) = extractFieldNames(classManifest[T]) jsonFormat(construct, a) } - def jsonFormat[A :JF, T <: Product](construct: A => T, a: String): RootJsonFormat[T] = new RootJsonFormat[T]{ + def jsonFormat[A :JF, T <: Product](construct: A => T, a : FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0) + productElement2Field[A](a._1, p, 0) ) def read(value: JsValue) = construct( fromField[A](value, a) @@ -50,10 +54,11 @@ trait ProductFormats { val Array(a, b) = extractFieldNames(classManifest[T]) jsonFormat(construct, a, b) } - def jsonFormat[A :JF, B :JF, T <: Product](construct: (A, B) => T, a: String, b: String): RootJsonFormat[T] = new RootJsonFormat[T]{ + + def jsonFormat[A :JF, B :JF, T <: Product](construct: (A, B) => T, a : FieldAndDefault, b: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1)) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1)) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -66,11 +71,11 @@ trait ProductFormats { jsonFormat(construct, a, b, c) } def jsonFormat[A :JF, B :JF, C :JF, T <: Product](construct: (A, B, C) => T, - a: String, b: String, c: String): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -85,12 +90,12 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d) } 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): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3)))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3)))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -106,13 +111,13 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e) } 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): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -129,14 +134,14 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f) } 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): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, f: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4, - productElement2Field[F](f, p, 5)))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4, + productElement2Field[F](f._1, p, 5)))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -154,15 +159,15 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g) } 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): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, f: FieldAndDefault, g: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4, - productElement2Field[F](f, p, 5, - productElement2Field[G](g, p, 6))))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4, + productElement2Field[F](f._1, p, 5, + productElement2Field[G](g._1, p, 6))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -182,16 +187,16 @@ trait ProductFormats { } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, T <: Product] (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): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4, - productElement2Field[F](f, p, 5, - productElement2Field[G](g, p, 6, - productElement2Field[H](h, p, 7)))))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4, + productElement2Field[F](f._1, p, 5, + productElement2Field[G](g._1, p, 6, + productElement2Field[H](h._1, p, 7)))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -211,18 +216,18 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, T <: Product] - (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): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, f: FieldAndDefault, + g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4, - productElement2Field[F](f, p, 5, - productElement2Field[G](g, p, 6, - productElement2Field[H](h, p, 7, - productElement2Field[I](i, p, 8))))))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4, + productElement2Field[F](f._1, p, 5, + productElement2Field[G](g._1, p, 6, + productElement2Field[H](h._1, p, 7, + productElement2Field[I](i._1, p, 8))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -243,19 +248,19 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, T <: Product] - (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): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, + f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4, - productElement2Field[F](f, p, 5, - productElement2Field[G](g, p, 6, - productElement2Field[H](h, p, 7, - productElement2Field[I](i, p, 8, - productElement2Field[J](j, p, 9)))))))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4, + productElement2Field[F](f._1, p, 5, + productElement2Field[G](g._1, p, 6, + productElement2Field[H](h._1, p, 7, + productElement2Field[I](i._1, p, 8, + productElement2Field[J](j._1, p, 9)))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -277,20 +282,20 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, T <: Product] - (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): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J, K) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, + f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault, k: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4, - productElement2Field[F](f, p, 5, - productElement2Field[G](g, p, 6, - productElement2Field[H](h, p, 7, - productElement2Field[I](i, p, 8, - productElement2Field[J](j, p, 9, - productElement2Field[K](k, p, 10))))))))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4, + productElement2Field[F](f._1, p, 5, + productElement2Field[G](g._1, p, 6, + productElement2Field[H](h._1, p, 7, + productElement2Field[I](i._1, p, 8, + productElement2Field[J](j._1, p, 9, + productElement2Field[K](k._1, p, 10))))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -313,21 +318,21 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, T <: Product] - (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): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J, K, L) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, + f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault, k: FieldAndDefault, l: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4, - productElement2Field[F](f, p, 5, - productElement2Field[G](g, p, 6, - productElement2Field[H](h, p, 7, - productElement2Field[I](i, p, 8, - productElement2Field[J](j, p, 9, - productElement2Field[K](k, p, 10, - productElement2Field[L](l, p, 11)))))))))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4, + productElement2Field[F](f._1, p, 5, + productElement2Field[G](g._1, p, 6, + productElement2Field[H](h._1, p, 7, + productElement2Field[I](i._1, p, 8, + productElement2Field[J](j._1, p, 9, + productElement2Field[K](k._1, p, 10, + productElement2Field[L](l._1, p, 11)))))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -351,22 +356,22 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, T <: Product] - (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): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, + f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault, k: FieldAndDefault, l: FieldAndDefault, m: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4, - productElement2Field[F](f, p, 5, - productElement2Field[G](g, p, 6, - productElement2Field[H](h, p, 7, - productElement2Field[I](i, p, 8, - productElement2Field[J](j, p, 9, - productElement2Field[K](k, p, 10, - productElement2Field[L](l, p, 11, - productElement2Field[M](m, p, 12))))))))))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4, + productElement2Field[F](f._1, p, 5, + productElement2Field[G](g._1, p, 6, + productElement2Field[H](h._1, p, 7, + productElement2Field[I](i._1, p, 8, + productElement2Field[J](j._1, p, 9, + productElement2Field[K](k._1, p, 10, + productElement2Field[L](l._1, p, 11, + productElement2Field[M](m._1, p, 12))))))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -391,24 +396,24 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, N :JF, T <: Product] - (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): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, + e: FieldAndDefault, f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault, k: FieldAndDefault, l: FieldAndDefault, m: FieldAndDefault, + n: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4, - productElement2Field[F](f, p, 5, - productElement2Field[G](g, p, 6, - productElement2Field[H](h, p, 7, - productElement2Field[I](i, p, 8, - productElement2Field[J](j, p, 9, - productElement2Field[K](k, p, 10, - productElement2Field[L](l, p, 11, - productElement2Field[M](m, p, 12, - productElement2Field[N](n, p, 13)))))))))))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4, + productElement2Field[F](f._1, p, 5, + productElement2Field[G](g._1, p, 6, + productElement2Field[H](h._1, p, 7, + productElement2Field[I](i._1, p, 8, + productElement2Field[J](j._1, p, 9, + productElement2Field[K](k._1, p, 10, + productElement2Field[L](l._1, p, 11, + productElement2Field[M](m._1, p, 12, + productElement2Field[N](n._1, p, 13)))))))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -434,25 +439,25 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, N :JF, O :JF, T <: Product] - (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): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, + e: FieldAndDefault, f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault, k: FieldAndDefault, l: FieldAndDefault, m: FieldAndDefault, n: FieldAndDefault, + o: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a, p, 0, - productElement2Field[B](b, p, 1, - productElement2Field[C](c, p, 2, - productElement2Field[D](d, p, 3, - productElement2Field[E](e, p, 4, - productElement2Field[F](f, p, 5, - productElement2Field[G](g, p, 6, - productElement2Field[H](h, p, 7, - productElement2Field[I](i, p, 8, - productElement2Field[J](j, p, 9, - productElement2Field[K](k, p, 10, - productElement2Field[L](l, p, 11, - productElement2Field[M](m, p, 12, - productElement2Field[N](n, p, 13, - productElement2Field[O](o, p, 14))))))))))))))) + productElement2Field[A](a._1, p, 0, + productElement2Field[B](b._1, p, 1, + productElement2Field[C](c._1, p, 2, + productElement2Field[D](d._1, p, 3, + productElement2Field[E](e._1, p, 4, + productElement2Field[F](f._1, p, 5, + productElement2Field[G](g._1, p, 6, + productElement2Field[H](h._1, p, 7, + productElement2Field[I](i._1, p, 8, + productElement2Field[J](j._1, p, 9, + productElement2Field[K](k._1, p, 10, + productElement2Field[L](l._1, p, 11, + productElement2Field[M](m._1, p, 12, + productElement2Field[N](n._1, p, 13, + productElement2Field[O](o._1, p, 14))))))))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -484,27 +489,33 @@ trait ProductFormats { } } - private def fromField[T](value: JsValue, fieldName: String)(implicit reader: JsonReader[T]) = { + private def fromField[T](value: JsValue, fieldAndDefault: FieldAndDefault)(implicit reader: JsonReader[T]) = { value match { case x: JsObject => - var fieldFound = false - try { - val fieldValue = x.fields(fieldName) - fieldFound = true - reader.read(fieldValue) - } - catch { - case e: NoSuchElementException if !fieldFound => - if (reader.isInstanceOf[OptionFormat[_]]) None.asInstanceOf[T] - else deserializationError("Object is missing required member '" + fieldName + "'", e) + x.fields.get(fieldAndDefault._1) match { + case Some(value) => + reader.read(value) + case None => fieldAndDefault._2 match { + case Some(defarg) => + defarg().asInstanceOf[T] + case None => + if (reader.isInstanceOf[OptionFormat[_]]) None.asInstanceOf[T] + else deserializationError("Object is missing required member '" + fieldAndDefault._1 + "'") + } } case _ => deserializationError("Object expected") } } - protected def extractFieldNames(classManifest: ClassManifest[_]): Array[String] = { + protected def extractFieldNames(classManifest: ClassManifest[_]): Array[(String, Option[() => Any])] = { val clazz = classManifest.erasure try { + // Need companion class for default arguments. + lazy val companionClass = Class.forName(clazz.getName + "$") + lazy val moduleField = + try { companionClass.getField("MODULE$") } + catch { case e : Throwable => throw new RuntimeException("Can't deserialize default arguments of nested case classes", e) } + lazy val companionObj = moduleField.get(null) // copy methods have the form copy$default$N(), we need to sort them in order, but must account for the fact // that lexical sorting of ...8(), ...9(), ...10() is not correct, so we extract N and sort by N.toInt val copyDefaultMethods = clazz.getMethods.filter(_.getName.startsWith("copy$default$")).sortBy( @@ -512,14 +523,37 @@ trait ProductFormats { val fields = clazz.getDeclaredFields.filterNot(f => f.getName.startsWith("$") || Modifier.isTransient(f.getModifiers)) if (copyDefaultMethods.length != fields.length) sys.error("Case class " + clazz.getName + " declares additional fields") + val applyDefaultMethods = copyDefaultMethods.map { method => + try { + val defmeth = companionClass.getMethod("apply" + method.getName.drop("copy".size)) + Some(() => defmeth.invoke(companionObj))} + catch { case e : Throwable => None } + } if (fields.zip(copyDefaultMethods).exists { case (f, m) => f.getType != m.getReturnType }) sys.error("Cannot determine field order of case class " + clazz.getName) - fields.map(_.getName) + fields.zip(applyDefaultMethods).map { case (f, m) => f.getName -> m } } catch { - case ex => throw new RuntimeException("Cannot automatically determine case class field names and order " + + case ex : Throwable => throw new RuntimeException("Cannot automatically determine case class field names and order " + "for '" + clazz.getName + "', please use the 'jsonFormat' overload with explicit field name specification", ex) } } + +// Abondoned reflection-based implement because of compatibility issues: +// def extractFields(tag: ClassTag[_]): Seq[(String, Option[() => Any])] = { +// val mirror = ru.runtimeMirror(getClass.getClassLoader) +// val clazz = mirror.classSymbol(tag.runtimeClass) +// val module = clazz.companionSymbol.asModule +// val im = mirror.reflect(mirror.reflectModule(module).instance) +// val ts = im.symbol.typeSignature +// val method = ts.member(newTermName("apply")).asMethod +// method.paramss.flatten.zipWithIndex.map { +// case (param, index) => +// ts.member(newTermName(s"apply$$default$$${index + 1}")) match { +// case NoSymbol => param.name.toString -> None +// case symbol => param.name.toString -> Some(() => im.reflectMethod(symbol.asMethod)()) +// } +// } +// } } /** diff --git a/src/test/scala/spray/json/ProductFormatsSpec.scala b/src/test/scala/spray/json/ProductFormatsSpec.scala index 5a07b4f9..77de1b6b 100644 --- a/src/test/scala/spray/json/ProductFormatsSpec.scala +++ b/src/test/scala/spray/json/ProductFormatsSpec.scala @@ -18,6 +18,9 @@ package spray.json import org.specs2.mutable._ + +case class TestDefault(a: Int = 123, b: String) + class ProductFormatsSpec extends Specification { case class Test2(a: Int, b: Option[Double]) @@ -31,6 +34,7 @@ class ProductFormatsSpec extends Specification { implicit val test2Format = jsonFormat2(Test2) implicit def test3Format[A: JsonFormat, B: JsonFormat] = jsonFormat2(Test3.apply[A, B]) implicit def testTransientFormat = jsonFormat2(TestTransient) + implicit def testDefaultFormat = jsonFormat2(TestDefault) } object TestProtocol1 extends DefaultJsonProtocol with TestProtocol object TestProtocol2 extends DefaultJsonProtocol with TestProtocol with NullOptions @@ -111,4 +115,16 @@ class ProductFormatsSpec extends Specification { } } + "A JsonFormat for a case class with fields with default values" should { + import TestProtocol1._ + val obj = TestDefault(b = "b") + + "evaluate the default argument expression when field is missing" in { + TestDefault(123, "b") === """{ "b":"b" }""".asJson.convertTo[TestDefault] + } + + "ignore the default argument when field is specified" in { + TestDefault(888, "b") === """{ "a" : 888, "b":"b" }""".asJson.convertTo[TestDefault] + } + } } From 7ea1017d42e8f0eb6d6ad4d407540e4fed72c698 Mon Sep 17 00:00:00 2001 From: Ruud Diterwich Date: Mon, 20 May 2013 15:01:41 +0200 Subject: [PATCH 2/5] Changes according to Johannes' comments --- .../scala/spray/json/ProductFormats.scala | 323 +++++++++--------- 1 file changed, 153 insertions(+), 170 deletions(-) diff --git a/src/main/scala/spray/json/ProductFormats.scala b/src/main/scala/spray/json/ProductFormats.scala index 8436e068..985a1aca 100644 --- a/src/main/scala/spray/json/ProductFormats.scala +++ b/src/main/scala/spray/json/ProductFormats.scala @@ -25,9 +25,9 @@ import java.lang.reflect.Modifier trait ProductFormats { this: StandardFormats => - type FieldAndDefault = (String, Option[() => Any]) - implicit def toFieldAndDefArg(s : String) = (s, None) - implicit def toFieldAndDefArg(s : (String, () => Any)) = (s._1, Some(s._2)) + case class Field(name: String, getDefault: Option[() => Any]) + implicit def toFieldAndDefArg(s : String) = Field(s, None) + implicit def toFieldAndDefArg(s : (String, () => Any)) = Field(s._1, Some(s._2)) def jsonFormat0[T <: Product :ClassManifest](construct: () => T): RootJsonFormat[T] = { jsonFormat(construct) @@ -41,9 +41,9 @@ trait ProductFormats { val Array(a) = extractFieldNames(classManifest[T]) jsonFormat(construct, a) } - def jsonFormat[A :JF, T <: Product](construct: A => T, a : FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + def jsonFormat[A :JF, T <: Product](construct: A => T, a : Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0) + productElement2Field[A](a.name, p, 0) ) def read(value: JsValue) = construct( fromField[A](value, a) @@ -55,10 +55,10 @@ trait ProductFormats { jsonFormat(construct, a, b) } - def jsonFormat[A :JF, B :JF, T <: Product](construct: (A, B) => T, a : FieldAndDefault, b: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + def jsonFormat[A :JF, B :JF, T <: Product](construct: (A, B) => T, a : Field, b: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1)) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1)) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -71,11 +71,11 @@ trait ProductFormats { jsonFormat(construct, a, b, c) } def jsonFormat[A :JF, B :JF, C :JF, T <: Product](construct: (A, B, C) => T, - a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : Field, b: Field, c: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -90,12 +90,12 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, T <: Product](construct: (A, B, C, D) => T, - a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : Field, b: Field, c: Field, d: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3)))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3)))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -111,13 +111,13 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, T <: Product](construct: (A, B, C, D, E) => T, - a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : Field, b: Field, c: Field, d: Field, e: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -134,14 +134,14 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f) } 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 : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, f: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : Field, b: Field, c: Field, d: Field, e: Field, f: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4, - productElement2Field[F](f._1, p, 5)))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4, + productElement2Field[F](f.name, p, 5)))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -159,15 +159,15 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g) } 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 : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, f: FieldAndDefault, g: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : Field, b: Field, c: Field, d: Field, e: Field, f: Field, g: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4, - productElement2Field[F](f._1, p, 5, - productElement2Field[G](g._1, p, 6))))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4, + productElement2Field[F](f.name, p, 5, + productElement2Field[G](g.name, p, 6))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -187,16 +187,16 @@ trait ProductFormats { } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, T <: Product] (construct: (A, B, C, D, E, F, G, H) => T, - a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + a : Field, b: Field, c: Field, d: Field, e: Field, f: Field, g: Field, h: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4, - productElement2Field[F](f._1, p, 5, - productElement2Field[G](g._1, p, 6, - productElement2Field[H](h._1, p, 7)))))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4, + productElement2Field[F](f.name, p, 5, + productElement2Field[G](g.name, p, 6, + productElement2Field[H](h.name, p, 7)))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -216,18 +216,18 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, T <: Product] - (construct: (A, B, C, D, E, F, G, H, I) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, f: FieldAndDefault, - g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I) => T, a : Field, b: Field, c: Field, d: Field, e: Field, f: Field, + g: Field, h: Field, i: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4, - productElement2Field[F](f._1, p, 5, - productElement2Field[G](g._1, p, 6, - productElement2Field[H](h._1, p, 7, - productElement2Field[I](i._1, p, 8))))))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4, + productElement2Field[F](f.name, p, 5, + productElement2Field[G](g.name, p, 6, + productElement2Field[H](h.name, p, 7, + productElement2Field[I](i.name, p, 8))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -248,19 +248,19 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, T <: Product] - (construct: (A, B, C, D, E, F, G, H, I, J) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, - f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J) => T, a : Field, b: Field, c: Field, d: Field, e: Field, + f: Field, g: Field, h: Field, i: Field, j: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4, - productElement2Field[F](f._1, p, 5, - productElement2Field[G](g._1, p, 6, - productElement2Field[H](h._1, p, 7, - productElement2Field[I](i._1, p, 8, - productElement2Field[J](j._1, p, 9)))))))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4, + productElement2Field[F](f.name, p, 5, + productElement2Field[G](g.name, p, 6, + productElement2Field[H](h.name, p, 7, + productElement2Field[I](i.name, p, 8, + productElement2Field[J](j.name, p, 9)))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -282,20 +282,20 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, T <: Product] - (construct: (A, B, C, D, E, F, G, H, I, J, K) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, - f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault, k: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J, K) => T, a : Field, b: Field, c: Field, d: Field, e: Field, + f: Field, g: Field, h: Field, i: Field, j: Field, k: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4, - productElement2Field[F](f._1, p, 5, - productElement2Field[G](g._1, p, 6, - productElement2Field[H](h._1, p, 7, - productElement2Field[I](i._1, p, 8, - productElement2Field[J](j._1, p, 9, - productElement2Field[K](k._1, p, 10))))))))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4, + productElement2Field[F](f.name, p, 5, + productElement2Field[G](g.name, p, 6, + productElement2Field[H](h.name, p, 7, + productElement2Field[I](i.name, p, 8, + productElement2Field[J](j.name, p, 9, + productElement2Field[K](k.name, p, 10))))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -318,21 +318,21 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, T <: Product] - (construct: (A, B, C, D, E, F, G, H, I, J, K, L) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, - f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault, k: FieldAndDefault, l: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J, K, L) => T, a : Field, b: Field, c: Field, d: Field, e: Field, + f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4, - productElement2Field[F](f._1, p, 5, - productElement2Field[G](g._1, p, 6, - productElement2Field[H](h._1, p, 7, - productElement2Field[I](i._1, p, 8, - productElement2Field[J](j._1, p, 9, - productElement2Field[K](k._1, p, 10, - productElement2Field[L](l._1, p, 11)))))))))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4, + productElement2Field[F](f.name, p, 5, + productElement2Field[G](g.name, p, 6, + productElement2Field[H](h.name, p, 7, + productElement2Field[I](i.name, p, 8, + productElement2Field[J](j.name, p, 9, + productElement2Field[K](k.name, p, 10, + productElement2Field[L](l.name, p, 11)))))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -356,22 +356,22 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, T <: Product] - (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, e: FieldAndDefault, - f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault, k: FieldAndDefault, l: FieldAndDefault, m: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M) => T, a : Field, b: Field, c: Field, d: Field, e: Field, + f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field, m: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4, - productElement2Field[F](f._1, p, 5, - productElement2Field[G](g._1, p, 6, - productElement2Field[H](h._1, p, 7, - productElement2Field[I](i._1, p, 8, - productElement2Field[J](j._1, p, 9, - productElement2Field[K](k._1, p, 10, - productElement2Field[L](l._1, p, 11, - productElement2Field[M](m._1, p, 12))))))))))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4, + productElement2Field[F](f.name, p, 5, + productElement2Field[G](g.name, p, 6, + productElement2Field[H](h.name, p, 7, + productElement2Field[I](i.name, p, 8, + productElement2Field[J](j.name, p, 9, + productElement2Field[K](k.name, p, 10, + productElement2Field[L](l.name, p, 11, + productElement2Field[M](m.name, p, 12))))))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -396,24 +396,24 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, N :JF, T <: Product] - (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, - e: FieldAndDefault, f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault, k: FieldAndDefault, l: FieldAndDefault, m: FieldAndDefault, - n: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N) => T, a : Field, b: Field, c: Field, d: Field, + e: Field, f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field, m: Field, + n: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4, - productElement2Field[F](f._1, p, 5, - productElement2Field[G](g._1, p, 6, - productElement2Field[H](h._1, p, 7, - productElement2Field[I](i._1, p, 8, - productElement2Field[J](j._1, p, 9, - productElement2Field[K](k._1, p, 10, - productElement2Field[L](l._1, p, 11, - productElement2Field[M](m._1, p, 12, - productElement2Field[N](n._1, p, 13)))))))))))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4, + productElement2Field[F](f.name, p, 5, + productElement2Field[G](g.name, p, 6, + productElement2Field[H](h.name, p, 7, + productElement2Field[I](i.name, p, 8, + productElement2Field[J](j.name, p, 9, + productElement2Field[K](k.name, p, 10, + productElement2Field[L](l.name, p, 11, + productElement2Field[M](m.name, p, 12, + productElement2Field[N](n.name, p, 13)))))))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -439,25 +439,25 @@ trait ProductFormats { jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, N :JF, O :JF, T <: Product] - (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => T, a : FieldAndDefault, b: FieldAndDefault, c: FieldAndDefault, d: FieldAndDefault, - e: FieldAndDefault, f: FieldAndDefault, g: FieldAndDefault, h: FieldAndDefault, i: FieldAndDefault, j: FieldAndDefault, k: FieldAndDefault, l: FieldAndDefault, m: FieldAndDefault, n: FieldAndDefault, - o: FieldAndDefault): RootJsonFormat[T] = new RootJsonFormat[T]{ + (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => T, a : Field, b: Field, c: Field, d: Field, + e: Field, f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field, m: Field, n: Field, + o: Field): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( - productElement2Field[A](a._1, p, 0, - productElement2Field[B](b._1, p, 1, - productElement2Field[C](c._1, p, 2, - productElement2Field[D](d._1, p, 3, - productElement2Field[E](e._1, p, 4, - productElement2Field[F](f._1, p, 5, - productElement2Field[G](g._1, p, 6, - productElement2Field[H](h._1, p, 7, - productElement2Field[I](i._1, p, 8, - productElement2Field[J](j._1, p, 9, - productElement2Field[K](k._1, p, 10, - productElement2Field[L](l._1, p, 11, - productElement2Field[M](m._1, p, 12, - productElement2Field[N](n._1, p, 13, - productElement2Field[O](o._1, p, 14))))))))))))))) + productElement2Field[A](a.name, p, 0, + productElement2Field[B](b.name, p, 1, + productElement2Field[C](c.name, p, 2, + productElement2Field[D](d.name, p, 3, + productElement2Field[E](e.name, p, 4, + productElement2Field[F](f.name, p, 5, + productElement2Field[G](g.name, p, 6, + productElement2Field[H](h.name, p, 7, + productElement2Field[I](i.name, p, 8, + productElement2Field[J](j.name, p, 9, + productElement2Field[K](k.name, p, 10, + productElement2Field[L](l.name, p, 11, + productElement2Field[M](m.name, p, 12, + productElement2Field[N](n.name, p, 13, + productElement2Field[O](o.name, p, 14))))))))))))))) ) def read(value: JsValue) = construct( fromField[A](value, a), @@ -489,25 +489,25 @@ trait ProductFormats { } } - private def fromField[T](value: JsValue, fieldAndDefault: FieldAndDefault)(implicit reader: JsonReader[T]) = { + private def fromField[T](value: JsValue, fieldAndDefault: Field)(implicit reader: JsonReader[T]) = { value match { case x: JsObject => - x.fields.get(fieldAndDefault._1) match { + x.fields.get(fieldAndDefault.name) match { case Some(value) => reader.read(value) - case None => fieldAndDefault._2 match { + case None => fieldAndDefault.getDefault match { case Some(defarg) => defarg().asInstanceOf[T] case None => if (reader.isInstanceOf[OptionFormat[_]]) None.asInstanceOf[T] - else deserializationError("Object is missing required member '" + fieldAndDefault._1 + "'") + else deserializationError("Object is missing required member '" + fieldAndDefault.name + "'") } } case _ => deserializationError("Object expected") } } - protected def extractFieldNames(classManifest: ClassManifest[_]): Array[(String, Option[() => Any])] = { + protected def extractFieldNames(classManifest: ClassManifest[_]): Array[Field] = { val clazz = classManifest.erasure try { // Need companion class for default arguments. @@ -531,29 +531,12 @@ trait ProductFormats { } if (fields.zip(copyDefaultMethods).exists { case (f, m) => f.getType != m.getReturnType }) sys.error("Cannot determine field order of case class " + clazz.getName) - fields.zip(applyDefaultMethods).map { case (f, m) => f.getName -> m } + fields.zip(applyDefaultMethods).map { case (f, m) => Field(f.getName, m) } } catch { case ex : Throwable => throw new RuntimeException("Cannot automatically determine case class field names and order " + "for '" + clazz.getName + "', please use the 'jsonFormat' overload with explicit field name specification", ex) } } - -// Abondoned reflection-based implement because of compatibility issues: -// def extractFields(tag: ClassTag[_]): Seq[(String, Option[() => Any])] = { -// val mirror = ru.runtimeMirror(getClass.getClassLoader) -// val clazz = mirror.classSymbol(tag.runtimeClass) -// val module = clazz.companionSymbol.asModule -// val im = mirror.reflect(mirror.reflectModule(module).instance) -// val ts = im.symbol.typeSignature -// val method = ts.member(newTermName("apply")).asMethod -// method.paramss.flatten.zipWithIndex.map { -// case (param, index) => -// ts.member(newTermName(s"apply$$default$$${index + 1}")) match { -// case NoSymbol => param.name.toString -> None -// case symbol => param.name.toString -> Some(() => im.reflectMethod(symbol.asMethod)()) -// } -// } -// } } /** From b13c2f2e3189fbf4af5ad001300a6edaefbbfc69 Mon Sep 17 00:00:00 2001 From: Ruud Diterwich Date: Mon, 20 May 2013 15:04:46 +0200 Subject: [PATCH 3/5] Small rename --- src/main/scala/spray/json/ProductFormats.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/spray/json/ProductFormats.scala b/src/main/scala/spray/json/ProductFormats.scala index 985a1aca..ce504688 100644 --- a/src/main/scala/spray/json/ProductFormats.scala +++ b/src/main/scala/spray/json/ProductFormats.scala @@ -26,8 +26,8 @@ trait ProductFormats { this: StandardFormats => case class Field(name: String, getDefault: Option[() => Any]) - implicit def toFieldAndDefArg(s : String) = Field(s, None) - implicit def toFieldAndDefArg(s : (String, () => Any)) = Field(s._1, Some(s._2)) + implicit def toField(s : String) = Field(s, None) + implicit def toField(s : (String, () => Any)) = Field(s._1, Some(s._2)) def jsonFormat0[T <: Product :ClassManifest](construct: () => T): RootJsonFormat[T] = { jsonFormat(construct) From e272c6f50ab642f2de4aebedaf8c03695bc0e589 Mon Sep 17 00:00:00 2001 From: Ruud Diterwich Date: Mon, 20 May 2013 15:26:21 +0200 Subject: [PATCH 4/5] Make partial document support optional --- .../scala/spray/json/ProductFormats.scala | 74 ++++++++++--------- .../scala/spray/json/ProductFormatsSpec.scala | 2 +- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/main/scala/spray/json/ProductFormats.scala b/src/main/scala/spray/json/ProductFormats.scala index ce504688..0c0fa203 100644 --- a/src/main/scala/spray/json/ProductFormats.scala +++ b/src/main/scala/spray/json/ProductFormats.scala @@ -29,16 +29,16 @@ trait ProductFormats { implicit def toField(s : String) = Field(s, None) implicit def toField(s : (String, () => Any)) = Field(s._1, Some(s._2)) - def jsonFormat0[T <: Product :ClassManifest](construct: () => T): RootJsonFormat[T] = { + def jsonFormat0[T <: Product :ClassManifest](construct: () => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { jsonFormat(construct) } - def jsonFormat[T <: Product](construct: () => T): RootJsonFormat[T] = new RootJsonFormat[T]{ + def jsonFormat[T <: Product](construct: () => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject() def read(value: JsValue) = construct() } - def jsonFormat1[A :JF, T <: Product :ClassManifest](construct: A => T): RootJsonFormat[T] = { - val Array(a) = extractFieldNames(classManifest[T]) + def jsonFormat1[A :JF, T <: Product :ClassManifest](construct: A => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a) } def jsonFormat[A :JF, T <: Product](construct: A => T, a : Field): RootJsonFormat[T] = new RootJsonFormat[T]{ @@ -50,8 +50,8 @@ trait ProductFormats { ) } - def jsonFormat2[A :JF, B :JF, T <: Product :ClassManifest](construct: (A, B) => T): RootJsonFormat[T] = { - val Array(a, b) = extractFieldNames(classManifest[T]) + def jsonFormat2[A :JF, B :JF, T <: Product :ClassManifest](construct: (A, B) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b) } @@ -66,8 +66,8 @@ trait ProductFormats { ) } - def jsonFormat3[A :JF, B :JF, C :JF, T <: Product :ClassManifest](construct: (A, B, C) => T): RootJsonFormat[T] = { - val Array(a, b, c) = extractFieldNames(classManifest[T]) + def jsonFormat3[A :JF, B :JF, C :JF, T <: Product :ClassManifest](construct: (A, B, C) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c) } def jsonFormat[A :JF, B :JF, C :JF, T <: Product](construct: (A, B, C) => T, @@ -85,8 +85,8 @@ trait ProductFormats { } def jsonFormat4[A :JF, B :JF, C :JF, D :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D) => T): RootJsonFormat[T] = { - val Array(a, b, c, d) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, T <: Product](construct: (A, B, C, D) => T, @@ -106,8 +106,8 @@ trait ProductFormats { } def jsonFormat5[A :JF, B :JF, C :JF, D :JF, E :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, T <: Product](construct: (A, B, C, D, E) => T, @@ -129,8 +129,8 @@ trait ProductFormats { } def jsonFormat6[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E, F) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e, f) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E, F) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e, f) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, T <: Product](construct: (A, B, C, D, E, F) => T, @@ -154,8 +154,8 @@ trait ProductFormats { } def jsonFormat7[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E, F, G) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e, f, g) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E, F, G) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e, f, g) } 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, @@ -181,8 +181,8 @@ trait ProductFormats { } def jsonFormat8[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E, F, G, H) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e, f, g, h) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E, F, G, H) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e, f, g, h) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, T <: Product] @@ -211,8 +211,8 @@ trait ProductFormats { } def jsonFormat9[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E, F, G, H, I) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e, f, g, h, i) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E, F, G, H, I) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e, f, g, h, i) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, T <: Product] @@ -243,8 +243,8 @@ trait ProductFormats { } def jsonFormat10[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E, F, G, H, I, J) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e, f, g, h, i, j) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E, F, G, H, I, J) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e, f, g, h, i, j) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, T <: Product] @@ -277,8 +277,8 @@ trait ProductFormats { } def jsonFormat11[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E, F, G, H, I, J, K) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e, f, g, h, i, j, k) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E, F, G, H, I, J, K) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, T <: Product] @@ -313,8 +313,8 @@ trait ProductFormats { } def jsonFormat12[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L: JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E, F, G, H, I, J, K, L) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e, f, g, h, i, j, k, l) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E, F, G, H, I, J, K, L) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, T <: Product] @@ -351,8 +351,8 @@ trait ProductFormats { } def jsonFormat13[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L: JF, M :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e, f, g, h, i, j, k, l, m) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l, m) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, T <: Product] @@ -391,8 +391,8 @@ trait ProductFormats { } def jsonFormat14[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L: JF, M :JF, N :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, N :JF, T <: Product] @@ -434,8 +434,8 @@ trait ProductFormats { } def jsonFormat15[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L: JF, M :JF, N :JF, O :JF, T <: Product :ClassManifest] - (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => T): RootJsonFormat[T] = { - val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = extractFieldNames(classManifest[T]) + (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = extractFieldNames(classManifest[T], allowOptionalFields) jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) } def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, N :JF, O :JF, T <: Product] @@ -507,7 +507,7 @@ trait ProductFormats { } } - protected def extractFieldNames(classManifest: ClassManifest[_]): Array[Field] = { + protected def extractFieldNames(classManifest: ClassManifest[_], allowOptionalFields: Boolean): Array[Field] = { val clazz = classManifest.erasure try { // Need companion class for default arguments. @@ -523,7 +523,7 @@ trait ProductFormats { val fields = clazz.getDeclaredFields.filterNot(f => f.getName.startsWith("$") || Modifier.isTransient(f.getModifiers)) if (copyDefaultMethods.length != fields.length) sys.error("Case class " + clazz.getName + " declares additional fields") - val applyDefaultMethods = copyDefaultMethods.map { method => + lazy val applyDefaultMethods = copyDefaultMethods.map { method => try { val defmeth = companionClass.getMethod("apply" + method.getName.drop("copy".size)) Some(() => defmeth.invoke(companionObj))} @@ -531,7 +531,11 @@ trait ProductFormats { } if (fields.zip(copyDefaultMethods).exists { case (f, m) => f.getType != m.getReturnType }) sys.error("Cannot determine field order of case class " + clazz.getName) - fields.zip(applyDefaultMethods).map { case (f, m) => Field(f.getName, m) } + if (allowOptionalFields) { + fields.zip(applyDefaultMethods).map { case (f, m) => Field(f.getName, m) } + } else { + fields.map { f => Field(f.getName, None) } + } } catch { case ex : Throwable => throw new RuntimeException("Cannot automatically determine case class field names and order " + "for '" + clazz.getName + "', please use the 'jsonFormat' overload with explicit field name specification", ex) diff --git a/src/test/scala/spray/json/ProductFormatsSpec.scala b/src/test/scala/spray/json/ProductFormatsSpec.scala index 77de1b6b..6c822a56 100644 --- a/src/test/scala/spray/json/ProductFormatsSpec.scala +++ b/src/test/scala/spray/json/ProductFormatsSpec.scala @@ -34,7 +34,7 @@ class ProductFormatsSpec extends Specification { implicit val test2Format = jsonFormat2(Test2) implicit def test3Format[A: JsonFormat, B: JsonFormat] = jsonFormat2(Test3.apply[A, B]) implicit def testTransientFormat = jsonFormat2(TestTransient) - implicit def testDefaultFormat = jsonFormat2(TestDefault) + implicit def testDefaultFormat = jsonFormat2(TestDefault, true) } object TestProtocol1 extends DefaultJsonProtocol with TestProtocol object TestProtocol2 extends DefaultJsonProtocol with TestProtocol with NullOptions From 4875c07a2a3e75757d9ae722d0ad8f2705b86168 Mon Sep 17 00:00:00 2001 From: Ruud Diterwich Date: Mon, 20 May 2013 16:04:03 +0200 Subject: [PATCH 5/5] Added formats up to 22 fields --- .../scala/spray/json/ProductFormats.scala | 350 ++++++++++++++++++ 1 file changed, 350 insertions(+) diff --git a/src/main/scala/spray/json/ProductFormats.scala b/src/main/scala/spray/json/ProductFormats.scala index 0c0fa203..8aaefc28 100644 --- a/src/main/scala/spray/json/ProductFormats.scala +++ b/src/main/scala/spray/json/ProductFormats.scala @@ -478,6 +478,356 @@ trait ProductFormats { ) } + def jsonFormat16[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, T <: Product: scala.reflect.ClassManifest](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) = extractFieldNames(classManifest[T], allowOptionalFields) + jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) + } + def jsonFormat[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, T <: Product](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) => T, a: Field, b: Field, c: Field, d: Field, + e: Field, f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field, m: Field, n: Field, + o: Field, p: Field): RootJsonFormat[T] = new RootJsonFormat[T] { + def write(pw: T) = JsObject( + productElement2Field[A](a.name, pw, 0, + productElement2Field[B](b.name, pw, 1, + productElement2Field[C](c.name, pw, 2, + productElement2Field[D](d.name, pw, 3, + productElement2Field[E](e.name, pw, 4, + productElement2Field[F](f.name, pw, 5, + productElement2Field[G](g.name, pw, 6, + productElement2Field[H](h.name, pw, 7, + productElement2Field[I](i.name, pw, 8, + productElement2Field[J](j.name, pw, 9, + productElement2Field[K](k.name, pw, 10, + productElement2Field[L](l.name, pw, 11, + productElement2Field[M](m.name, pw, 12, + productElement2Field[N](n.name, pw, 13, + productElement2Field[O](o.name, pw, 14, + productElement2Field[P](p.name, pw, 15))))))))))))))))) + def read(value: JsValue) = construct( + 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), + fromField[P](value, p)) + } + + def jsonFormat17[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, T <: Product: scala.reflect.ClassManifest](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) = extractFieldNames(classManifest[T], allowOptionalFields) + jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) + } + def jsonFormat[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, T <: Product](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) => T, a: Field, b: Field, c: Field, d: Field, + e: Field, f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field, m: Field, n: Field, + o: Field, p: Field, q: Field): RootJsonFormat[T] = new RootJsonFormat[T] { + def write(pw: T) = JsObject( + productElement2Field[A](a.name, pw, 0, + productElement2Field[B](b.name, pw, 1, + productElement2Field[C](c.name, pw, 2, + productElement2Field[D](d.name, pw, 3, + productElement2Field[E](e.name, pw, 4, + productElement2Field[F](f.name, pw, 5, + productElement2Field[G](g.name, pw, 6, + productElement2Field[H](h.name, pw, 7, + productElement2Field[I](i.name, pw, 8, + productElement2Field[J](j.name, pw, 9, + productElement2Field[K](k.name, pw, 10, + productElement2Field[L](l.name, pw, 11, + productElement2Field[M](m.name, pw, 12, + productElement2Field[N](n.name, pw, 13, + productElement2Field[O](o.name, pw, 14, + productElement2Field[P](p.name, pw, 15, + productElement2Field[Q](q.name, pw, 16)))))))))))))))))) + + def read(value: JsValue) = construct( + 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), + fromField[P](value, p), + fromField[Q](value, q)) + } + + + def jsonFormat18[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, R: JF, T <: Product: scala.reflect.ClassManifest](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) = extractFieldNames(classManifest[T], allowOptionalFields) + jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) + } + def jsonFormat[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, R: JF, T <: Product](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) => T, a: Field, b: Field, c: Field, d: Field, + e: Field, f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field, m: Field, n: Field, + o: Field, p: Field, q: Field, r: Field): RootJsonFormat[T] = new RootJsonFormat[T] { + def write(pw: T) = JsObject( + productElement2Field[A](a.name, pw, 0, + productElement2Field[B](b.name, pw, 1, + productElement2Field[C](c.name, pw, 2, + productElement2Field[D](d.name, pw, 3, + productElement2Field[E](e.name, pw, 4, + productElement2Field[F](f.name, pw, 5, + productElement2Field[G](g.name, pw, 6, + productElement2Field[H](h.name, pw, 7, + productElement2Field[I](i.name, pw, 8, + productElement2Field[J](j.name, pw, 9, + productElement2Field[K](k.name, pw, 10, + productElement2Field[L](l.name, pw, 11, + productElement2Field[M](m.name, pw, 12, + productElement2Field[N](n.name, pw, 13, + productElement2Field[O](o.name, pw, 14, + productElement2Field[P](p.name, pw, 15, + productElement2Field[Q](q.name, pw, 16, + productElement2Field[R](r.name, pw, 17))))))))))))))))))) + + def read(value: JsValue) = construct( + 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), + fromField[P](value, p), + fromField[Q](value, q), + fromField[R](value, r)) + } + + def jsonFormat19[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, R: JF, S: JF, T <: Product: scala.reflect.ClassManifest](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) = extractFieldNames(classManifest[T], allowOptionalFields) + jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) + } + + def jsonFormat[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, R: JF, S: JF, T <: Product](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) => T, a: Field, b: Field, c: Field, d: Field, + e: Field, f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field, m: Field, n: Field, + o: Field, p: Field, q: Field, r: Field, s: Field): RootJsonFormat[T] = new RootJsonFormat[T] { + def write(pw: T) = JsObject( + productElement2Field[A](a.name, pw, 0, + productElement2Field[B](b.name, pw, 1, + productElement2Field[C](c.name, pw, 2, + productElement2Field[D](d.name, pw, 3, + productElement2Field[E](e.name, pw, 4, + productElement2Field[F](f.name, pw, 5, + productElement2Field[G](g.name, pw, 6, + productElement2Field[H](h.name, pw, 7, + productElement2Field[I](i.name, pw, 8, + productElement2Field[J](j.name, pw, 9, + productElement2Field[K](k.name, pw, 10, + productElement2Field[L](l.name, pw, 11, + productElement2Field[M](m.name, pw, 12, + productElement2Field[N](n.name, pw, 13, + productElement2Field[O](o.name, pw, 14, + productElement2Field[P](p.name, pw, 15, + productElement2Field[Q](q.name, pw, 16, + productElement2Field[R](r.name, pw, 17, + productElement2Field[S](s.name, pw, 18)))))))))))))))))))) + def read(value: JsValue) = construct( + 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), + fromField[P](value, p), + fromField[Q](value, q), + fromField[R](value, r), + fromField[S](value, s)) + } + + def jsonFormat20[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, R: JF, S: JF, U: JF, T <: Product: scala.reflect.ClassManifest](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, U) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, u) = extractFieldNames(classManifest[T], allowOptionalFields) + jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, u) + } + + def jsonFormat[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, R: JF, S: JF, U: JF, T <: Product](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, U) => T, a: Field, b: Field, c: Field, d: Field, + e: Field, f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field, m: Field, n: Field, + o: Field, p: Field, q: Field, r: Field, s: Field, u: Field): RootJsonFormat[T] = new RootJsonFormat[T] { + def write(pw: T) = JsObject( + productElement2Field[A](a.name, pw, 0, + productElement2Field[B](b.name, pw, 1, + productElement2Field[C](c.name, pw, 2, + productElement2Field[D](d.name, pw, 3, + productElement2Field[E](e.name, pw, 4, + productElement2Field[F](f.name, pw, 5, + productElement2Field[G](g.name, pw, 6, + productElement2Field[H](h.name, pw, 7, + productElement2Field[I](i.name, pw, 8, + productElement2Field[J](j.name, pw, 9, + productElement2Field[K](k.name, pw, 10, + productElement2Field[L](l.name, pw, 11, + productElement2Field[M](m.name, pw, 12, + productElement2Field[N](n.name, pw, 13, + productElement2Field[O](o.name, pw, 14, + productElement2Field[P](p.name, pw, 15, + productElement2Field[Q](q.name, pw, 16, + productElement2Field[R](r.name, pw, 17, + productElement2Field[S](s.name, pw, 18, + productElement2Field[U](u.name, pw, 19))))))))))))))))))))) + def read(value: JsValue) = construct( + 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), + fromField[P](value, p), + fromField[Q](value, q), + fromField[R](value, r), + fromField[S](value, s), + fromField[U](value, u)) + } + + def jsonFormat21[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, R: JF, S: JF, U: JF, V: JF, T <: Product: scala.reflect.ClassManifest](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, U, V) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, u, v) = extractFieldNames(classManifest[T], allowOptionalFields) + jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, u, v) + } + + def jsonFormat[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, R: JF, S: JF, U: JF, V: JF, T <: Product](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, U, V) => T, a: Field, b: Field, c: Field, d: Field, + e: Field, f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field, m: Field, n: Field, + o: Field, p: Field, q: Field, r: Field, s: Field, u: Field, v: Field): RootJsonFormat[T] = new RootJsonFormat[T] { + def write(pw: T) = JsObject( + productElement2Field[A](a.name, pw, 0, + productElement2Field[B](b.name, pw, 1, + productElement2Field[C](c.name, pw, 2, + productElement2Field[D](d.name, pw, 3, + productElement2Field[E](e.name, pw, 4, + productElement2Field[F](f.name, pw, 5, + productElement2Field[G](g.name, pw, 6, + productElement2Field[H](h.name, pw, 7, + productElement2Field[I](i.name, pw, 8, + productElement2Field[J](j.name, pw, 9, + productElement2Field[K](k.name, pw, 10, + productElement2Field[L](l.name, pw, 11, + productElement2Field[M](m.name, pw, 12, + productElement2Field[N](n.name, pw, 13, + productElement2Field[O](o.name, pw, 14, + productElement2Field[P](p.name, pw, 15, + productElement2Field[Q](q.name, pw, 16, + productElement2Field[R](r.name, pw, 17, + productElement2Field[S](s.name, pw, 18, + productElement2Field[U](u.name, pw, 19, + productElement2Field[V](v.name, pw, 20)))))))))))))))))))))) + def read(value: JsValue) = construct( + 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), + fromField[P](value, p), + fromField[Q](value, q), + fromField[R](value, r), + fromField[S](value, s), + fromField[U](value, u), + fromField[V](value, v)) + } + + def jsonFormat22[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, R: JF, S: JF, U: JF, V: JF, W: JF, T <: Product: scala.reflect.ClassManifest](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, U, V, W) => T, allowOptionalFields: Boolean = false): RootJsonFormat[T] = { + val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, u, v, w) = extractFieldNames(classManifest[T], allowOptionalFields) + jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, u, v, w) + } + + def jsonFormat[A: JF, B: JF, C: JF, D: JF, E: JF, F: JF, G: JF, H: JF, I: JF, J: JF, K: JF, L: JF, M: JF, N: JF, O: JF, P: JF, Q: JF, R: JF, S: JF, U: JF, V: JF, W: JF, T <: Product](construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, U, V, W) => T, a: Field, b: Field, c: Field, d: Field, + e: Field, f: Field, g: Field, h: Field, i: Field, j: Field, k: Field, l: Field, m: Field, n: Field, + o: Field, p: Field, q: Field, r: Field, s: Field, u: Field, v: Field, w: Field): RootJsonFormat[T] = new RootJsonFormat[T] { + def write(pw: T) = JsObject( + productElement2Field[A](a.name, pw, 0, + productElement2Field[B](b.name, pw, 1, + productElement2Field[C](c.name, pw, 2, + productElement2Field[D](d.name, pw, 3, + productElement2Field[E](e.name, pw, 4, + productElement2Field[F](f.name, pw, 5, + productElement2Field[G](g.name, pw, 6, + productElement2Field[H](h.name, pw, 7, + productElement2Field[I](i.name, pw, 8, + productElement2Field[J](j.name, pw, 9, + productElement2Field[K](k.name, pw, 10, + productElement2Field[L](l.name, pw, 11, + productElement2Field[M](m.name, pw, 12, + productElement2Field[N](n.name, pw, 13, + productElement2Field[O](o.name, pw, 14, + productElement2Field[P](p.name, pw, 15, + productElement2Field[Q](q.name, pw, 16, + productElement2Field[R](r.name, pw, 17, + productElement2Field[S](s.name, pw, 18, + productElement2Field[U](u.name, pw, 19, + productElement2Field[V](v.name, pw, 20, + productElement2Field[W](w.name, pw, 21))))))))))))))))))))))) + def read(value: JsValue) = construct( + 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), + fromField[P](value, p), + fromField[Q](value, q), + fromField[R](value, r), + fromField[S](value, s), + fromField[U](value, u), + fromField[V](value, v), + fromField[W](value, w)) + } + // helpers protected def productElement2Field[T](fieldName: String, p: Product, ix: Int, rest: List[JsField] = Nil)