diff options
Diffstat (limited to 'src/main/scala/spray/json/ProductFormats.scala')
-rw-r--r-- | src/main/scala/spray/json/ProductFormats.scala | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/src/main/scala/spray/json/ProductFormats.scala b/src/main/scala/spray/json/ProductFormats.scala new file mode 100644 index 0000000..5504fbd --- /dev/null +++ b/src/main/scala/spray/json/ProductFormats.scala @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2011 Mathias Doenitz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package spray.json + +/** + * Provides the helpers for constructing custom JsonFormat implementations for types implementing the Product trait + * (especially case classes) + */ +trait ProductFormats { + this: StandardFormats => + + 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) + ) + def read(value: JsValue) = construct( + fromField[A](value, a) + ) + } + + 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)) + ) + def read(value: JsValue) = construct( + fromField[A](value, a), + fromField[B](value, b) + ) + } + + 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): 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))) + ) + def read(value: JsValue) = construct( + fromField[A](value, a), + fromField[B](value, b), + fromField[C](value, c) + ) + } + + 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): 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)))) + ) + def read(value: JsValue) = construct( + fromField[A](value, a), + fromField[B](value, b), + fromField[C](value, c), + fromField[D](value, d) + ) + } + + 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): 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))))) + ) + 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) + ) + } + + 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): 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)))))) + ) + 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) + ) + } + + 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): 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))))))) + ) + 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) + ) + } + + 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): 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)))))))) + ) + 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) + ) + } + + 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): 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))))))))) + ) + 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) + ) + } + + 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): 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)))))))))) + ) + 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) + ) + } + + 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): 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))))))))))) + ) + 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) + ) + } + + 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): 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)))))))))))) + ) + 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) + ) + } + + 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): 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))))))))))))) + ) + 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) + ) + } + + 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): 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)))))))))))))) + ) + 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) + ) + } + + 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): 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))))))))))))))) + ) + 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) + ) + } + + // helpers + + protected def productElement2Field[T](fieldName: String, p: Product, ix: Int, rest: List[JsField] = Nil) + (implicit writer: JsonWriter[T]): List[JsField] = { + val value = p.productElement(ix).asInstanceOf[T] + writer match { + case _: OptionFormat[_] if (value == None) => rest + case _ => (fieldName, writer.write(value)) :: rest + } + } + + private def fromField[T](value: JsValue, fieldName: String)(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) + } + case _ => deserializationError("Object expected") + } + } + + protected def extractFieldNames(classManifest: ClassManifest[_]): Array[String] = { + val clazz = classManifest.erasure + try { + // 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( + _.getName.drop("copy$default$".length).takeWhile(_ != '(').toInt) + val fields = clazz.getDeclaredFields.filterNot(_.getName.startsWith("$")) + if (copyDefaultMethods.length != fields.length) + sys.error("Case class " + clazz.getName + " declares additional fields") + 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) + } catch { + case ex => 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) + } + } +} + +/** + * This trait supplies an alternative rendering mode for optional case class members. + * Normally optional members that are undefined (`None`) are not rendered at all. + * By mixing in this trait into your custom JsonProtocol you can enforce the rendering of undefined members as `null`. + * (Note that this only affect JSON writing, spray-json will always read missing optional members as well as `null` + * optional members as `None`.) + */ +trait NullOptions extends ProductFormats { + this: StandardFormats => + + override protected def productElement2Field[T](fieldName: String, p: Product, ix: Int, rest: List[JsField]) + (implicit writer: JsonWriter[T]) = { + val value = p.productElement(ix).asInstanceOf[T] + (fieldName, writer.write(value)) :: rest + } +} |