From c6b5edcb5665b0ef60b5108d8cff3431c0663927 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 19 Jan 2012 16:48:46 +0100 Subject: Add jsonFormatX overloads providing automatic case class field name extraction Conflicts: src/main/scala/cc/spray/json/ProductFormats.scala --- src/main/scala/cc/spray/json/ProductFormats.scala | 132 +++++++++++++++++---- .../scala/cc/spray/json/ProductFormatsSpec.scala | 4 +- 2 files changed, 112 insertions(+), 24 deletions(-) diff --git a/src/main/scala/cc/spray/json/ProductFormats.scala b/src/main/scala/cc/spray/json/ProductFormats.scala index 6a79f5f..1f26259 100644 --- a/src/main/scala/cc/spray/json/ProductFormats.scala +++ b/src/main/scala/cc/spray/json/ProductFormats.scala @@ -23,7 +23,11 @@ package cc.spray.json trait ProductFormats { this: StandardFormats => - def jsonFormat[A :JF, T <: Product](construct: A => T, a: String) = new RootJsonFormat[T]{ + def jsonFormat1[A :JF, T <: Product :ClassManifest](construct: A => T): RootJsonFormat[T] = { + 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 write(p: T) = JsObject( productElement2Field[A](a, p, 0) ) @@ -32,7 +36,11 @@ trait ProductFormats { ) } - def jsonFormat[A :JF, B :JF, T <: Product](construct: (A, B) => T, a: String, b: String) = new RootJsonFormat[T]{ + def jsonFormat2[A :JF, B :JF, T <: Product :ClassManifest](construct: (A, B) => T): RootJsonFormat[T] = { + 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 write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1)) @@ -43,8 +51,12 @@ 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]) + 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) = new RootJsonFormat[T]{ + a: String, b: String, c: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -57,8 +69,13 @@ 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]) + 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) = new RootJsonFormat[T]{ + a: String, b: String, c: String, d: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -73,8 +90,13 @@ 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]) + 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) = new RootJsonFormat[T]{ + a: String, b: String, c: String, d: String, e: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -90,9 +112,14 @@ trait ProductFormats { fromField[E](value, e) ) } - + + 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]) + 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) = new RootJsonFormat[T]{ + a: String, b: String, c: String, d: String, e: String, f: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -110,9 +137,14 @@ trait ProductFormats { fromField[F](value, f) ) } - + + 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]) + 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) = new RootJsonFormat[T]{ + a: String, b: String, c: String, d: String, e: String, f: String, g: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -132,10 +164,15 @@ trait ProductFormats { fromField[G](value, g) ) } - + + 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]) + 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] (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 RootJsonFormat[T]{ + a: String, b: String, c: String, d: String, e: String, f: String, g: String, h: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -157,10 +194,15 @@ trait ProductFormats { fromField[H](value, h) ) } - + + 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]) + 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) = new RootJsonFormat[T]{ + g: String, h: String, i: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -184,10 +226,15 @@ trait ProductFormats { fromField[I](value, i) ) } - + + 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]) + 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) = new RootJsonFormat[T]{ + f: String, g: String, h: String, i: String, j: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -213,10 +260,15 @@ trait ProductFormats { fromField[J](value, j) ) } - + + 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]) + 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) = new RootJsonFormat[T]{ + f: String, g: String, h: String, i: String, j: String, k: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -245,9 +297,14 @@ 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]) + 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) = new RootJsonFormat[T]{ + f: String, g: String, h: String, i: String, j: String, k: String, l: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -278,9 +335,14 @@ 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]) + 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) = new RootJsonFormat[T]{ + f: String, g: String, h: String, i: String, j: String, k: String, l: String, m: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -313,10 +375,15 @@ 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]) + 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) = new RootJsonFormat[T]{ + n: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -351,10 +418,15 @@ 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]) + 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) = new RootJsonFormat[T]{ + o: String): RootJsonFormat[T] = new RootJsonFormat[T]{ def write(p: T) = JsObject( productElement2Field[A](a, p, 0, productElement2Field[B](b, p, 1, @@ -401,7 +473,7 @@ trait ProductFormats { case _ => (fieldName, writer.write(value)) :: rest } } - + private def fromField[T](value: JsValue, fieldName: String)(implicit reader: JsonReader[T]) = { value match { case x: JsObject => @@ -419,6 +491,22 @@ trait ProductFormats { case _ => deserializationError("Object expected") } } + + protected def extractFieldNames(classManifest: ClassManifest[_]): Array[String] = { + val clazz = classManifest.erasure + try { + val copyDefaultMethods = clazz.getMethods.filter(_.getName.startsWith("copy$default$")) + val fields = clazz.getDeclaredFields.filterNot(_.getName.startsWith("$")) + if (copyDefaultMethods.length != fields.length) + sys.error("Case class declares additional fields") + if (fields.zip(copyDefaultMethods).exists { case (f, m) => f.getType != m.getReturnType }) + sys.error("Cannot determine field order") + fields.map(_.getName) + } catch { + case ex => throw new RuntimeException("Cannot automatically determine case class field names and order, " + + "please use the 'jsonFormat' overload with explicit field name specification", ex) + } + } } /** diff --git a/src/test/scala/cc/spray/json/ProductFormatsSpec.scala b/src/test/scala/cc/spray/json/ProductFormatsSpec.scala index 6ec1611..c34f491 100644 --- a/src/test/scala/cc/spray/json/ProductFormatsSpec.scala +++ b/src/test/scala/cc/spray/json/ProductFormatsSpec.scala @@ -25,8 +25,8 @@ class ProductFormatsSpec extends Specification { trait TestProtocol { this: DefaultJsonProtocol => - implicit val test2Format = jsonFormat(Test2, "a", "b") - implicit def test3Format[A: JsonFormat, B: JsonFormat] = jsonFormat(Test3.apply[A, B], "as", "bs") + implicit val test2Format = jsonFormat2(Test2) + implicit def test3Format[A: JsonFormat, B: JsonFormat] = jsonFormat2(Test3.apply[A, B]) } object TestProtocol1 extends DefaultJsonProtocol with TestProtocol object TestProtocol2 extends DefaultJsonProtocol with TestProtocol with NullOptions -- cgit v1.2.3