summaryrefslogtreecommitdiff
path: root/src/main/scala/cc/spray
diff options
context:
space:
mode:
authorMathias <mathias@spray.cc>2011-05-06 11:11:37 +0200
committerMathias <mathias@spray.cc>2011-05-06 11:11:37 +0200
commit0ce9cf8fce1dc475f3bb2a517e0a7698c9e0a5d9 (patch)
tree62de608642a30ec478411d27e94179ec64c5bd17 /src/main/scala/cc/spray
downloadspray-json-0ce9cf8fce1dc475f3bb2a517e0a7698c9e0a5d9.tar.gz
spray-json-0ce9cf8fce1dc475f3bb2a517e0a7698c9e0a5d9.tar.bz2
spray-json-0ce9cf8fce1dc475f3bb2a517e0a7698c9e0a5d9.zip
Initial commit (split off from main spray codebase)
Diffstat (limited to 'src/main/scala/cc/spray')
-rw-r--r--src/main/scala/cc/spray/json/CompactPrinter.scala47
-rw-r--r--src/main/scala/cc/spray/json/DeserializationException.scala19
-rw-r--r--src/main/scala/cc/spray/json/JsValue.scala128
-rw-r--r--src/main/scala/cc/spray/json/JsonParser.scala121
-rw-r--r--src/main/scala/cc/spray/json/JsonPrinter.scala75
-rw-r--r--src/main/scala/cc/spray/json/PimpedAny.scala25
-rw-r--r--src/main/scala/cc/spray/json/PrettyPrinter.scala64
-rw-r--r--src/main/scala/cc/spray/json/SerializationException.scala19
-rw-r--r--src/main/scala/cc/spray/json/formats/BasicFormats.scala125
-rw-r--r--src/main/scala/cc/spray/json/formats/CollectionFormats.scala69
-rw-r--r--src/main/scala/cc/spray/json/formats/DefaultJsonFormats.scala23
-rw-r--r--src/main/scala/cc/spray/json/formats/GenericFormats.scala260
-rw-r--r--src/main/scala/cc/spray/json/formats/JsonFormat.scala29
-rw-r--r--src/main/scala/cc/spray/json/formats/StandardFormats.scala101
-rw-r--r--src/main/scala/cc/spray/json/package.scala12
15 files changed, 1117 insertions, 0 deletions
diff --git a/src/main/scala/cc/spray/json/CompactPrinter.scala b/src/main/scala/cc/spray/json/CompactPrinter.scala
new file mode 100644
index 0000000..b64597a
--- /dev/null
+++ b/src/main/scala/cc/spray/json/CompactPrinter.scala
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2009-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 cc.spray.json
+
+import java.lang.StringBuilder
+
+object CompactPrinter extends JsonPrinter {
+
+ def print(x: JsValue, sb: StringBuilder) {
+ x match {
+ case JsObject(x) => printObject(x, sb)
+ case JsArray(x) => printArray(x, sb)
+ case _ => printLeaf(x, sb)
+ }
+ }
+
+ private def printObject(members: List[JsField], sb: StringBuilder) {
+ sb.append('{')
+ printSeq(members, sb.append(',')) { m =>
+ printString(m.name, sb)
+ sb.append(':')
+ print(m.value, sb)
+ }
+ sb.append('}')
+ }
+
+ private def printArray(elements: List[JsValue], sb: StringBuilder) {
+ sb.append('[')
+ printSeq(elements, sb.append(','))(print(_, sb))
+ sb.append(']')
+ }
+
+} \ No newline at end of file
diff --git a/src/main/scala/cc/spray/json/DeserializationException.scala b/src/main/scala/cc/spray/json/DeserializationException.scala
new file mode 100644
index 0000000..283d996
--- /dev/null
+++ b/src/main/scala/cc/spray/json/DeserializationException.scala
@@ -0,0 +1,19 @@
+/*
+ * 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 cc.spray.json
+
+class DeserializationException(msg: String) extends RuntimeException(msg) \ No newline at end of file
diff --git a/src/main/scala/cc/spray/json/JsValue.scala b/src/main/scala/cc/spray/json/JsValue.scala
new file mode 100644
index 0000000..8781f73
--- /dev/null
+++ b/src/main/scala/cc/spray/json/JsValue.scala
@@ -0,0 +1,128 @@
+/*
+ * Original implementation (C) by the databinder-dispatch team
+ * https://github.com/n8han/Databinder-Dispatch
+ * Adapted and extended in 2011 by Mathias Doenitz
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cc.spray.json
+
+import formats._
+import collection.mutable.ListBuffer
+
+sealed trait JsValue {
+ override def toString = CompactPrinter(this)
+ def toString(printer: (JsValue => String)) = printer(this)
+ def fromJson[T :JsonReader]: T = jsonReader.read(this)
+}
+
+object JsValue {
+ def apply(x: Any): JsValue = x match {
+ case null => JsNull
+ case true => JsTrue
+ case false => JsFalse
+ case x: JsValue => x
+ case x: String => JsString(x)
+ case x: Symbol => JsString(x.name)
+ case x: Int => JsNumber(x)
+ case x: Long => JsNumber(x)
+ case x: Short => JsNumber(x)
+ case x: Byte => JsNumber(x)
+ case x: Float => JsNumber(x)
+ case x: Double => JsNumber(x)
+ case x: BigInt => JsNumber(x)
+ case x: BigDecimal => JsNumber(x)
+ case x: Char => JsString(String.valueOf(x))
+ case x: collection.Map[_, _] => JsObject(fromSeq(x))
+ case x@ collection.Seq((_, _), _*) => JsObject(fromSeq(x.asInstanceOf[Seq[(_, _)]]))
+ case x: collection.Seq[_] => JsArray(x.toList.map(JsValue.apply))
+ case x => throw new IllegalArgumentException(x.toString + " cannot be converted to a JsValue")
+ }
+
+ private def fromSeq(seq: Iterable[(_, _)]) = {
+ val list = ListBuffer.empty[JsField]
+ seq.foreach {
+ case (key: String, value) => list += JsField(key, JsValue(value))
+ case (key: Symbol, value) => list += JsField(key.name, JsValue(value))
+ case (key: JsString, value) => list += JsField(key.value, JsValue(value))
+ case (x, _) => throw new IllegalArgumentException(x.toString + " cannot be converted to a JsString")
+ }
+ list.toList
+ }
+
+ def fromString(json: String) = JsonParser(json)
+ def toString(value: JsValue, printer: (JsValue => String) = CompactPrinter) = printer(value)
+}
+
+case class JsString(value: String) extends JsValue
+
+
+case class JsNumber(value: BigDecimal) extends JsValue
+
+object JsNumber {
+ def apply(n: Int) = new JsNumber(BigDecimal(n))
+ def apply(n: Long) = new JsNumber(BigDecimal(n))
+ def apply(n: Double) = new JsNumber(BigDecimal(n))
+ def apply(n: BigInt) = new JsNumber(BigDecimal(n))
+ def apply(n: String) = new JsNumber(BigDecimal(n))
+}
+
+case class JsObject(fields: List[JsField]) extends JsValue {
+ lazy val asMap: Map[String, JsValue] = {
+ val b = Map.newBuilder[String, JsValue]
+ for (JsField(name, value) <- fields) b += ((name, value))
+ b.result()
+ }
+}
+
+object JsObject {
+ def apply(members: JsField*) = new JsObject(members.toList)
+}
+
+
+case class JsField(name: String, value: JsValue) extends JsValue
+
+object JsField {
+ def apply(name: String, value: Any) = new JsField(name, JsValue(value))
+}
+
+
+case class JsArray(elements: List[JsValue]) extends JsValue
+
+object JsArray {
+ def apply(elements: JsValue*) = new JsArray(elements.toList)
+}
+
+
+sealed trait JsBoolean extends JsValue {
+ def value: Boolean
+}
+
+object JsBoolean {
+ def apply(x: Boolean): JsBoolean = if (x) JsTrue else JsFalse
+ def unapply(x: JsBoolean): Option[Boolean] = Some(x.value)
+}
+
+case object JsTrue extends JsBoolean {
+ def value = true
+}
+
+case object JsFalse extends JsBoolean {
+ def value = false
+}
+
+
+case object JsNull extends JsValue
diff --git a/src/main/scala/cc/spray/json/JsonParser.scala b/src/main/scala/cc/spray/json/JsonParser.scala
new file mode 100644
index 0000000..275f310
--- /dev/null
+++ b/src/main/scala/cc/spray/json/JsonParser.scala
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2009-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 cc.spray.json
+
+import org.parboiled.scala._
+import org.parboiled.errors.{ErrorUtils, ParsingException}
+import java.lang.StringBuilder
+import org.parboiled.Context
+
+/**
+ * This JSON parser is the almost direct implementation of the JSON grammar
+ * presented at http://www.json.org as a parboiled PEG parser.
+ */
+object JsonParser extends Parser {
+
+ // the root rule
+ def Json = rule { WhiteSpace ~ Value ~ EOI }
+
+ def JsonObject: Rule1[JsObject] = rule {
+ "{ " ~ zeroOrMore(Pair, separator = ", ") ~ "} " ~~> (JsObject(_))
+ }
+
+ def Pair = rule { JsonStringUnwrapped ~ ": " ~ Value ~~> (JsField(_, _)) }
+
+ def Value: Rule1[JsValue] = rule {
+ JsonString | JsonNumber | JsonObject | JsonArray | JsonTrue | JsonFalse | JsonNull
+ }
+
+ def JsonString = rule { JsonStringUnwrapped ~~> JsString }
+
+ def JsonStringUnwrapped = rule { "\"" ~ Characters ~ "\" " ~~> (_.toString) }
+
+ def JsonNumber = rule { group(Integer ~ optional(Frac) ~ optional(Exp)) ~> (JsNumber(_)) ~ WhiteSpace }
+
+ def JsonArray = rule { "[ " ~ zeroOrMore(Value, separator = ", ") ~ "] " ~~> (JsArray(_)) }
+
+ def Characters = rule { push(new StringBuilder) ~ zeroOrMore("\\" ~ EscapedChar | NormalChar) }
+
+ def EscapedChar = rule (
+ anyOf("\"\\/") ~:% withContext(appendToSb(_)(_))
+ | "b" ~ appendToSb('\b')
+ | "f" ~ appendToSb('\f')
+ | "n" ~ appendToSb('\n')
+ | "r" ~ appendToSb('\r')
+ | "t" ~ appendToSb('\t')
+ | Unicode ~~% withContext((code, ctx) => appendToSb(code.asInstanceOf[Char])(ctx))
+ )
+
+ def NormalChar = rule { !anyOf("\"\\") ~ ANY ~:% (withContext(appendToSb(_)(_))) }
+
+ def Unicode = rule { "u" ~ group(HexDigit ~ HexDigit ~ HexDigit ~ HexDigit) ~> (java.lang.Integer.parseInt(_, 16)) }
+
+ def Integer = rule { optional("-") ~ (("1" - "9") ~ Digits | Digit) }
+
+ def Digits = rule { oneOrMore(Digit) }
+
+ def Digit = rule { "0" - "9" }
+
+ def HexDigit = rule { "0" - "9" | "a" - "f" | "A" - "Z" }
+
+ def Frac = rule { "." ~ Digits }
+
+ def Exp = rule { ignoreCase("e") ~ optional(anyOf("+-")) ~ Digits }
+
+ def JsonTrue = rule { "true " ~ push(JsTrue) }
+
+ def JsonFalse = rule { "false " ~ push(JsFalse) }
+
+ def JsonNull = rule { "null " ~ push(JsNull) }
+
+ def WhiteSpace: Rule0 = rule { zeroOrMore(anyOf(" \n\r\t\f")) }
+
+ // helper method for fast string building
+ // for maximum performance we use a somewhat unorthodox parsing technqiue that is a bit more verbose (and probably
+ // less readable) but reduces object allocations during the parsing run to a minimum:
+ // the Characters rules pushes a StringBuilder object onto the stack which is then directly fed with matched
+ // and unescaped characters in the sub rules (i.e. no string allocations and value stack operation required)
+ def appendToSb(c: Char): Context[Any] => Unit = { ctx =>
+ ctx.getValueStack.peek.asInstanceOf[StringBuilder].append(c)
+ ()
+ }
+
+ /**
+ * We redefine the default string-to-rule conversion to also match trailing whitespace if the string ends with
+ * a blank, this keeps the rules free from most whitespace matching clutter
+ */
+ override implicit def toRule(string: String) = {
+ if (string.endsWith(" ")) str(string.trim) ~ WhiteSpace
+ else str(string)
+ }
+
+ /**
+ * The main parsing method. Uses a ReportingParseRunner (which only reports the first error) for simplicity.
+ */
+ def apply(json: String): JsValue = apply(json.toCharArray)
+
+ /**
+ * The main parsing method. Uses a ReportingParseRunner (which only reports the first error) for simplicity.
+ */
+ def apply(json: Array[Char]): JsValue = {
+ val parsingResult = ReportingParseRunner(Json).run(json)
+ parsingResult.result.getOrElse {
+ throw new ParsingException("Invalid JSON source:\n" + ErrorUtils.printParseErrors(parsingResult))
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/scala/cc/spray/json/JsonPrinter.scala b/src/main/scala/cc/spray/json/JsonPrinter.scala
new file mode 100644
index 0000000..7751b7e
--- /dev/null
+++ b/src/main/scala/cc/spray/json/JsonPrinter.scala
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009-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 cc.spray.json
+
+import annotation.tailrec
+import java.lang.StringBuilder
+
+trait JsonPrinter extends (JsValue => String) {
+
+ def apply(x: JsValue) = {
+ val sb = new StringBuilder
+ print(x, sb)
+ sb.toString
+ }
+
+ def print(x: JsValue, sb: StringBuilder)
+
+ protected def printLeaf(x: JsValue, sb: StringBuilder) {
+ x match {
+ case JsNull => sb.append("null")
+ case JsTrue => sb.append("true")
+ case JsFalse => sb.append("false")
+ case JsNumber(x) => sb.append(x)
+ case JsString(x) => printString(x, sb)
+ case _ => throw new IllegalStateException
+ }
+ }
+
+ protected def printString(s: String, sb: StringBuilder) {
+ @tailrec
+ def printEscaped(s: String, ix: Int) {
+ if (ix < s.length) {
+ s.charAt(ix) match {
+ case '"' => sb.append("\\\"")
+ case '\\' => sb.append("\\\\")
+ case x if 0x20 <= x && x < 0x7F => sb.append(x)
+ case '\b' => sb.append("\\b")
+ case '\f' => sb.append("\\f")
+ case '\n' => sb.append("\\n")
+ case '\r' => sb.append("\\r")
+ case '\t' => sb.append("\\t")
+ case x if x <= 0xFF => sb.append("\\u00").append(Integer.toHexString(x))
+ case x if x <= 0xFFF => sb.append("\\u0").append(Integer.toHexString(x))
+ case x => sb.append("\\u").append(Integer.toHexString(x))
+ }
+ printEscaped(s, ix + 1)
+ }
+ }
+ sb.append('"')
+ printEscaped(s, 0)
+ sb.append('"')
+ }
+
+ protected def printSeq[A](iterable: Iterable[A], printSeparator: => Unit)(f: A => Unit) {
+ var first = true
+ iterable.foreach { a =>
+ if (first) first = false else printSeparator
+ f(a)
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/scala/cc/spray/json/PimpedAny.scala b/src/main/scala/cc/spray/json/PimpedAny.scala
new file mode 100644
index 0000000..86a18ce
--- /dev/null
+++ b/src/main/scala/cc/spray/json/PimpedAny.scala
@@ -0,0 +1,25 @@
+package cc.spray.json
+
+/*
+ * Copyright (C) 2009-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.
+ */
+
+import formats.JsonWriter
+
+private[json] class PimpedAny[T](any: T, writer: JsonWriter[T]) {
+
+ def toJson: JsValue = writer.write(any)
+
+} \ No newline at end of file
diff --git a/src/main/scala/cc/spray/json/PrettyPrinter.scala b/src/main/scala/cc/spray/json/PrettyPrinter.scala
new file mode 100644
index 0000000..f0fcf14
--- /dev/null
+++ b/src/main/scala/cc/spray/json/PrettyPrinter.scala
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009-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 cc.spray.json
+
+import java.lang.StringBuilder
+import annotation.tailrec
+
+object PrettyPrinter extends JsonPrinter {
+ val Indent = 2
+
+ def print(x: JsValue, sb: StringBuilder) {
+ print(x, sb, 0)
+ }
+
+ private def print(x: JsValue, sb: StringBuilder, indent: Int) {
+ x match {
+ case JsObject(x) => printObject(x, sb, indent)
+ case JsArray(x) => printArray(x, sb, indent)
+ case _ => printLeaf(x, sb)
+ }
+ }
+
+ private def printObject(members: List[JsField], sb: StringBuilder, indent: Int) {
+ sb.append("{\n")
+ printSeq(members, sb.append(",\n")) { m =>
+ printIndent(sb, indent + Indent)
+ printString(m.name, sb)
+ sb.append(": ")
+ print(m.value, sb, indent + Indent)
+ }
+ sb.append('\n')
+ printIndent(sb, indent)
+ sb.append("}")
+ }
+
+ private def printArray(elements: List[JsValue], sb: StringBuilder, indent: Int) {
+ sb.append('[')
+ printSeq(elements, sb.append(", "))(print(_, sb, indent))
+ sb.append(']')
+ }
+
+ @tailrec
+ private def printIndent(sb: StringBuilder, indent: Int) {
+ if (indent > 0) {
+ sb.append(' ')
+ printIndent(sb, indent - 1)
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/scala/cc/spray/json/SerializationException.scala b/src/main/scala/cc/spray/json/SerializationException.scala
new file mode 100644
index 0000000..f8cbc00
--- /dev/null
+++ b/src/main/scala/cc/spray/json/SerializationException.scala
@@ -0,0 +1,19 @@
+/*
+ * 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 cc.spray.json
+
+class SerializationException(msg: String) extends RuntimeException(msg) \ No newline at end of file
diff --git a/src/main/scala/cc/spray/json/formats/BasicFormats.scala b/src/main/scala/cc/spray/json/formats/BasicFormats.scala
new file mode 100644
index 0000000..378d67d
--- /dev/null
+++ b/src/main/scala/cc/spray/json/formats/BasicFormats.scala
@@ -0,0 +1,125 @@
+package cc.spray.json
+package formats
+
+/*
+ * Original implementation (C) 2009-2011 Debasish Ghosh
+ * Adapted and extended in 2011 by 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.
+ */
+
+trait BasicFormats {
+
+ implicit object IntJsonFormat extends JsonFormat[Int] {
+ def write(x: Int) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.intValue
+ case _ => throw new DeserializationException("Int expected")
+ }
+ }
+
+ implicit object LongJsonFormat extends JsonFormat[Long] {
+ def write(x: Long) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.longValue
+ case _ => throw new DeserializationException("Long expected")
+ }
+ }
+
+ implicit object FloatJsonFormat extends JsonFormat[Float] {
+ def write(x: Float) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.floatValue
+ case _ => throw new DeserializationException("Float expected")
+ }
+ }
+
+ implicit object DoubleJsonFormat extends JsonFormat[Double] {
+ def write(x: Double) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.doubleValue
+ case _ => throw new DeserializationException("Double expected")
+ }
+ }
+
+ implicit object ByteJsonFormat extends JsonFormat[Byte] {
+ def write(x: Byte) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.byteValue
+ case _ => throw new DeserializationException("Byte expected")
+ }
+ }
+
+ implicit object ShortJsonFormat extends JsonFormat[Short] {
+ def write(x: Short) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.shortValue
+ case _ => throw new DeserializationException("Short expected")
+ }
+ }
+
+ implicit object BigDecimalJsonFormat extends JsonFormat[BigDecimal] {
+ def write(x: BigDecimal) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x
+ case _ => throw new DeserializationException("String expected")
+ }
+ }
+
+ implicit object BigIntJsonFormat extends JsonFormat[BigInt] {
+ def write(x: BigInt) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.toBigInt
+ case _ => throw new DeserializationException("BigInt expected")
+ }
+ }
+
+ implicit object UnitJsonFormat extends JsonFormat[Unit] {
+ def write(x: Unit) = JsNumber(1)
+ def read(value: JsValue) {}
+ }
+
+ implicit object BooleanJsonFormat extends JsonFormat[Boolean] {
+ def write(x: Boolean) = JsBoolean(x)
+ def read(value: JsValue) = value match {
+ case JsTrue => true
+ case JsFalse => false
+ case _ => throw new DeserializationException("Boolean expected")
+ }
+ }
+
+ implicit object CharJsonFormat extends JsonFormat[Char] {
+ def write(x: Char) = JsString(String.valueOf(x))
+ def read(value: JsValue) = value match {
+ case JsString(x) if x.length == 1 => x.charAt(0)
+ case _ => throw new DeserializationException("Char expected")
+ }
+ }
+
+ implicit object StringJsonFormat extends JsonFormat[String] {
+ def write(x: String) = JsString(x)
+ def read(value: JsValue) = value match {
+ case JsString(x) => x
+ case _ => throw new DeserializationException("String expected")
+ }
+ }
+
+ implicit object SymbolJsonFormat extends JsonFormat[Symbol] {
+ def write(x: Symbol) = JsString(x.name)
+ def read(value: JsValue) = value match {
+ case JsString(x) => Symbol(x)
+ case _ => throw new DeserializationException("Symbol expected")
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/scala/cc/spray/json/formats/CollectionFormats.scala b/src/main/scala/cc/spray/json/formats/CollectionFormats.scala
new file mode 100644
index 0000000..53be0d5
--- /dev/null
+++ b/src/main/scala/cc/spray/json/formats/CollectionFormats.scala
@@ -0,0 +1,69 @@
+package cc.spray.json
+package formats
+
+/*
+ * Original implementation (C) 2009-2011 Debasish Ghosh
+ * Adapted and extended in 2011 by 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.
+ */
+
+import reflect.Manifest
+
+trait CollectionFormats {
+
+ implicit def listFormat[T :JsonFormat] = new JsonFormat[List[T]] {
+ def write(list: List[T]) = JsArray(list.map(_.toJson))
+ def read(value: JsValue) = value match {
+ case JsArray(elements) => elements.map(_.fromJson)
+ case _ => throw new DeserializationException("List expected")
+ }
+ }
+
+ implicit def arrayFormat[T :JsonFormat :Manifest] = new JsonFormat[Array[T]] {
+ def write(array: Array[T]) = JsArray(array.map(_.toJson).toList)
+ def read(value: JsValue) = value match {
+ case JsArray(elements) => elements.map(_.fromJson[T]).toArray
+ case _ => throw new DeserializationException("Array expected")
+ }
+ }
+
+ implicit def mapFormat[K :JsonFormat, V :JsonFormat] = new JsonFormat[Map[K, V]] {
+ def write(m: Map[K, V]) = JsObject {
+ m.toList.map { t =>
+ t._1.toJson match {
+ case JsString(x) => JsField(x, t._2.toJson)
+ case x => throw new SerializationException("Map key must be formatted as JsString, not '" + x + "'")
+ }
+ }
+ }
+ def read(value: JsValue) = value match {
+ case JsObject(fields) => fields.map(field => (JsString(field.name).fromJson[K], field.value.fromJson[V])).toMap
+ case _ => throw new DeserializationException("Map expected")
+ }
+ }
+
+ implicit def immutableSetFormat[T :JsonFormat] = viaList[Set[T], T](list => Set(list :_*))
+
+ import collection.mutable.Set
+ implicit def mutableSetFormat[T :JsonFormat] = viaList[Set[T], T](list => Set.empty ++ list)
+
+ def viaList[I <: Iterable[T], T :JsonFormat](f: List[T] => I): JsonFormat[I] = new JsonFormat[I] {
+ def write(iterable: I) = JsArray(iterable.map(_.toJson).toList)
+ def read(value: JsValue) = value match {
+ case JsArray(elements) => f(elements.map(_.fromJson[T]))
+ case _ => throw new DeserializationException("Collection expected")
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/scala/cc/spray/json/formats/DefaultJsonFormats.scala b/src/main/scala/cc/spray/json/formats/DefaultJsonFormats.scala
new file mode 100644
index 0000000..868ebb7
--- /dev/null
+++ b/src/main/scala/cc/spray/json/formats/DefaultJsonFormats.scala
@@ -0,0 +1,23 @@
+package cc.spray.json
+package formats
+
+/*
+ * Original implementation (C) 2009-2011 Debasish Ghosh
+ * Adapted and extended in 2011 by 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.
+ */
+
+trait DefaultJsonFormats extends BasicFormats with StandardFormats with CollectionFormats with GenericFormats
+
+object DefaultJsonFormats extends DefaultJsonFormats
diff --git a/src/main/scala/cc/spray/json/formats/GenericFormats.scala b/src/main/scala/cc/spray/json/formats/GenericFormats.scala
new file mode 100644
index 0000000..52aa837
--- /dev/null
+++ b/src/main/scala/cc/spray/json/formats/GenericFormats.scala
@@ -0,0 +1,260 @@
+package cc.spray.json
+package formats
+
+/*
+ * Original implementation (C) 2009-2011 Debasish Ghosh
+ * Adapted and extended in 2011 by 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.
+ */
+
+trait GenericFormats {
+
+ private type JF[T] = JsonFormat[T] // simple alias for reduced verbosity
+
+ /**
+ * Lazy wrapper around serialization. Useful when you want to serialize mutually recursive structures.
+ */
+ def lazyFormat[T](format: => JF[T]) = new JF[T]{
+ lazy val delegate = format;
+ def write(x: T) = delegate.write(x);
+ def read(value: JsValue) = delegate.read(value);
+ }
+
+ def format[A :JF, B :JF, T <: Product](construct: (A, B) => T, a: String, b: String) = new JF[T]{
+ def write(p: T) = JsObject(
+ JsField(a, element[A](p, 0).toJson),
+ JsField(b, element[B](p, 1).toJson)
+ )
+ def read(value: JsValue) = construct(
+ field(value, a).fromJson[A],
+ field(value, b).fromJson[B]
+ )
+ }
+
+ def format[A :JF, B :JF, C :JF, T <: Product](construct: (A, B, C) => T,
+ a: String, b: String, c: String) = new JF[T]{
+ def write(p: T) = JsObject(
+ JsField(a, element[A](p, 0).toJson),
+ JsField(b, element[B](p, 1).toJson),
+ JsField(c, element[C](p, 2).toJson)
+ )
+ def read(value: JsValue) = construct(
+ field(value, a).fromJson[A],
+ field(value, b).fromJson[B],
+ field(value, c).fromJson[C]
+ )
+ }
+
+ def format[A :JF, B :JF, C :JF, D :JF, T <: Product](construct: (A, B, C, D) => T,
+ a: String, b: String, c: String, d: String) = new JF[T]{
+ def write(p: T) = JsObject(
+ JsField(a, element[A](p, 0).toJson),
+ JsField(b, element[B](p, 1).toJson),
+ JsField(c, element[C](p, 2).toJson),
+ JsField(d, element[D](p, 3).toJson)
+ )
+ def read(value: JsValue) = construct(
+ field(value, a).fromJson[A],
+ field(value, b).fromJson[B],
+ field(value, c).fromJson[C],
+ field(value, d).fromJson[D]
+ )
+ }
+
+ def format[A :JF, B :JF, C :JF, D :JF, E :JF, T <: Product](construct: (A, B, C, D, E) => T,
+ a: String, b: String, c: String, d: String, e: String) = new JF[T]{
+ def write(p: T) = JsObject(
+ JsField(a, element[A](p, 0).toJson),
+ JsField(b, element[B](p, 1).toJson),
+ JsField(c, element[C](p, 2).toJson),
+ JsField(d, element[D](p, 3).toJson),
+ JsField(e, element[E](p, 4).toJson)
+ )
+ def read(value: JsValue) = construct(
+ field(value, a).fromJson[A],
+ field(value, b).fromJson[B],
+ field(value, c).fromJson[C],
+ field(value, d).fromJson[D],
+ field(value, e).fromJson[E]
+ )
+ }
+
+ def format[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, T <: Product](construct: (A, B, C, D, E, F) => T,
+ a: String, b: String, c: String, d: String, e: String, f: String) = new JF[T]{
+ def write(p: T) = JsObject(
+ JsField(a, element[A](p, 0).toJson),
+ JsField(b, element[B](p, 1).toJson),
+ JsField(c, element[C](p, 2).toJson),
+ JsField(d, element[D](p, 3).toJson),
+ JsField(e, element[E](p, 4).toJson),
+ JsField(f, element[F](p, 5).toJson)
+ )
+ def read(value: JsValue) = construct(
+ field(value, a).fromJson[A],
+ field(value, b).fromJson[B],
+ field(value, c).fromJson[C],
+ field(value, d).fromJson[D],
+ field(value, e).fromJson[E],
+ field(value, f).fromJson[F]
+ )
+ }
+
+ def format[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, T <: Product](construct: (A, B, C, D, E, F, G) => T,
+ a: String, b: String, c: String, d: String, e: String, f: String, g: String) = new JF[T]{
+ def write(p: T) = JsObject(
+ JsField(a, element[A](p, 0).toJson),
+ JsField(b, element[B](p, 1).toJson),
+ JsField(c, element[C](p, 2).toJson),
+ JsField(d, element[D](p, 3).toJson),
+ JsField(e, element[E](p, 4).toJson),
+ JsField(f, element[F](p, 5).toJson),
+ JsField(g, element[G](p, 6).toJson)
+ )
+ def read(value: JsValue) = construct(
+ field(value, a).fromJson[A],
+ field(value, b).fromJson[B],
+ field(value, c).fromJson[C],
+ field(value, d).fromJson[D],
+ field(value, e).fromJson[E],
+ field(value, f).fromJson[F],
+ field(value, g).fromJson[G]
+ )
+ }
+
+ def format[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 JF[T]{
+ def write(p: T) = JsObject(
+ JsField(a, element[A](p, 0).toJson),
+ JsField(b, element[B](p, 1).toJson),
+ JsField(c, element[C](p, 2).toJson),
+ JsField(d, element[D](p, 3).toJson),
+ JsField(e, element[E](p, 4).toJson),
+ JsField(f, element[F](p, 5).toJson),
+ JsField(g, element[G](p, 6).toJson),
+ JsField(h, element[H](p, 7).toJson)
+ )
+ def read(value: JsValue) = construct(
+ field(value, a).fromJson[A],
+ field(value, b).fromJson[B],
+ field(value, c).fromJson[C],
+ field(value, d).fromJson[D],
+ field(value, e).fromJson[E],
+ field(value, f).fromJson[F],
+ field(value, g).fromJson[G],
+ field(value, h).fromJson[H]
+ )
+ }
+
+ def format[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 JF[T]{
+ def write(p: T) = JsObject(
+ JsField(a, element[A](p, 0).toJson),
+ JsField(b, element[B](p, 1).toJson),
+ JsField(c, element[C](p, 2).toJson),
+ JsField(d, element[D](p, 3).toJson),
+ JsField(e, element[E](p, 4).toJson),
+ JsField(f, element[F](p, 5).toJson),
+ JsField(g, element[G](p, 6).toJson),
+ JsField(h, element[H](p, 7).toJson),
+ JsField(i, element[I](p, 8).toJson)
+ )
+ def read(value: JsValue) = construct(
+ field(value, a).fromJson[A],
+ field(value, b).fromJson[B],
+ field(value, c).fromJson[C],
+ field(value, d).fromJson[D],
+ field(value, e).fromJson[E],
+ field(value, f).fromJson[F],
+ field(value, g).fromJson[G],
+ field(value, h).fromJson[H],
+ field(value, i).fromJson[I]
+ )
+ }
+
+ def format[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 JF[T]{
+ def write(p: T) = JsObject(
+ JsField(a, element[A](p, 0).toJson),
+ JsField(b, element[B](p, 1).toJson),
+ JsField(c, element[C](p, 2).toJson),
+ JsField(d, element[D](p, 3).toJson),
+ JsField(e, element[E](p, 4).toJson),
+ JsField(f, element[F](p, 5).toJson),
+ JsField(g, element[G](p, 6).toJson),
+ JsField(h, element[H](p, 7).toJson),
+ JsField(i, element[I](p, 8).toJson),
+ JsField(j, element[J](p, 9).toJson)
+ )
+ def read(value: JsValue) = construct(
+ field(value, a).fromJson[A],
+ field(value, b).fromJson[B],
+ field(value, c).fromJson[C],
+ field(value, d).fromJson[D],
+ field(value, e).fromJson[E],
+ field(value, f).fromJson[F],
+ field(value, g).fromJson[G],
+ field(value, h).fromJson[H],
+ field(value, i).fromJson[I],
+ field(value, j).fromJson[J]
+ )
+ }
+
+ def format[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 JF[T]{
+ def write(p: T) = JsObject(
+ JsField(a, element[A](p, 0).toJson),
+ JsField(b, element[B](p, 1).toJson),
+ JsField(c, element[C](p, 2).toJson),
+ JsField(d, element[D](p, 3).toJson),
+ JsField(e, element[E](p, 4).toJson),
+ JsField(f, element[F](p, 5).toJson),
+ JsField(g, element[G](p, 6).toJson),
+ JsField(h, element[H](p, 7).toJson),
+ JsField(i, element[I](p, 8).toJson),
+ JsField(j, element[J](p, 9).toJson),
+ JsField(k, element[K](p, 10).toJson)
+ )
+ def read(value: JsValue) = construct(
+ field(value, a).fromJson[A],
+ field(value, b).fromJson[B],
+ field(value, c).fromJson[C],
+ field(value, d).fromJson[D],
+ field(value, e).fromJson[E],
+ field(value, f).fromJson[F],
+ field(value, g).fromJson[G],
+ field(value, h).fromJson[H],
+ field(value, i).fromJson[I],
+ field(value, j).fromJson[J],
+ field(value, k).fromJson[K]
+ )
+ }
+
+ // helpers
+
+ private def element[T](p: Product, ix: Int) = p.productElement(ix).asInstanceOf[T]
+
+ private def field(value: JsValue, fieldName: String) = value match {
+ case jso: JsObject => {
+ jso.fields
+ .find(_.name == fieldName)
+ .getOrElse(throw new DeserializationException("Object is missing required member '" + fieldName + "'"))
+ .value
+ }
+ case _ => throw new DeserializationException("Object expected")
+ }
+}
diff --git a/src/main/scala/cc/spray/json/formats/JsonFormat.scala b/src/main/scala/cc/spray/json/formats/JsonFormat.scala
new file mode 100644
index 0000000..1edac19
--- /dev/null
+++ b/src/main/scala/cc/spray/json/formats/JsonFormat.scala
@@ -0,0 +1,29 @@
+package cc.spray.json
+package formats
+
+/*
+ * Original implementation (C) 2009-2011 Debasish Ghosh
+ * Adapted and extended in 2011 by 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.
+ */
+
+trait JsonReader[T] {
+ def read(json: JsValue): T
+}
+
+trait JsonWriter[T] {
+ def write(obj: T): JsValue
+}
+
+trait JsonFormat[T] extends JsonReader[T] with JsonWriter[T]
diff --git a/src/main/scala/cc/spray/json/formats/StandardFormats.scala b/src/main/scala/cc/spray/json/formats/StandardFormats.scala
new file mode 100644
index 0000000..91e21d3
--- /dev/null
+++ b/src/main/scala/cc/spray/json/formats/StandardFormats.scala
@@ -0,0 +1,101 @@
+package cc.spray.json
+package formats
+
+/*
+ * Original implementation (C) 2009-2011 Debasish Ghosh
+ * Adapted and extended in 2011 by 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.
+ */
+
+trait StandardFormats {
+
+ private type JF[T] = JsonFormat[T] // simple alias for reduced verbosity
+
+ implicit def optionFormat[T :JF] = new JF[Option[T]] {
+ def write(option: Option[T]) = option match {
+ case Some(x) => x.toJson
+ case None => JsNull
+ }
+ def read(value: JsValue) = value match {
+ case JsNull => None
+ case x => Some(x.fromJson)
+ }
+ }
+
+ implicit def tuple1Format[A :JF] = new JF[Tuple1[A]] {
+ def write(t: Tuple1[A]) = t._1.toJson
+ def read(value: JsValue) = Tuple1(value.fromJson[A])
+ }
+
+ implicit def tuple2Format[A :JF, B :JF] = new JF[(A, B)] {
+ def write(t: (A, B)) = JsArray(t._1.toJson, t._2.toJson)
+ def read(value: JsValue) = value match {
+ case JsArray(a :: b :: Nil) => (a.fromJson[A], b.fromJson[B])
+ case _ => throw new DeserializationException("Tuple2 expected")
+ }
+ }
+
+ implicit def tuple3Format[A :JF, B :JF, C :JF] = new JF[(A, B, C)] {
+ def write(t: (A, B, C)) = JsArray(t._1.toJson, t._2.toJson, t._3.toJson)
+ def read(value: JsValue) = value match {
+ case JsArray(a :: b :: c :: Nil) => (a.fromJson[A], b.fromJson[B], c.fromJson[C])
+ case _ => throw new DeserializationException("Tuple3 expected")
+ }
+ }
+
+ implicit def tuple4Format[A :JF, B :JF, C :JF, D :JF] = new JF[(A, B, C, D)] {
+ def write(t: (A, B, C, D)) = JsArray(t._1.toJson, t._2.toJson, t._3.toJson, t._4.toJson)
+ def read(value: JsValue) = value match {
+ case JsArray(a :: b :: c :: d :: Nil) => (a.fromJson[A], b.fromJson[B], c.fromJson[C], d.fromJson[D])
+ case _ => throw new DeserializationException("Tuple4 expected")
+ }
+ }
+
+ implicit def tuple5Format[A :JF, B :JF, C :JF, D :JF, E :JF] = {
+ new JF[(A, B, C, D, E)] {
+ def write(t: (A, B, C, D, E)) = JsArray(t._1.toJson, t._2.toJson, t._3.toJson, t._4.toJson, t._5.toJson)
+ def read(value: JsValue) = value match {
+ case JsArray(a :: b :: c :: d :: e :: Nil) => {
+ (a.fromJson[A], b.fromJson[B], c.fromJson[C], d.fromJson[D], e.fromJson[E])
+ }
+ case _ => throw new DeserializationException("Tuple5 expected")
+ }
+ }
+ }
+
+ implicit def tuple6Format[A :JF, B :JF, C :JF, D :JF, E :JF, F: JF] = {
+ new JF[(A, B, C, D, E, F)] {
+ def write(t: (A, B, C, D, E, F)) = JsArray(t._1.toJson, t._2.toJson, t._3.toJson, t._4.toJson, t._5.toJson, t._6.toJson)
+ def read(value: JsValue) = value match {
+ case JsArray(a :: b :: c :: d :: e :: f :: Nil) => {
+ (a.fromJson[A], b.fromJson[B], c.fromJson[C], d.fromJson[D], e.fromJson[E], f.fromJson[F])
+ }
+ case _ => throw new DeserializationException("Tuple6 expected")
+ }
+ }
+ }
+
+ implicit def tuple7Format[A :JF, B :JF, C :JF, D :JF, E :JF, F: JF, G: JF] = {
+ new JF[(A, B, C, D, E, F, G)] {
+ def write(t: (A, B, C, D, E, F, G)) = JsArray(t._1.toJson, t._2.toJson, t._3.toJson, t._4.toJson, t._5.toJson, t._6.toJson, t._6.toJson)
+ def read(value: JsValue) = value match {
+ case JsArray(a :: b :: c :: d :: e :: f :: g :: Nil) => {
+ (a.fromJson[A], b.fromJson[B], c.fromJson[C], d.fromJson[D], e.fromJson[E], f.fromJson[F], g.fromJson[G])
+ }
+ case _ => throw new DeserializationException("Tuple7 expected")
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/scala/cc/spray/json/package.scala b/src/main/scala/cc/spray/json/package.scala
new file mode 100644
index 0000000..1932315
--- /dev/null
+++ b/src/main/scala/cc/spray/json/package.scala
@@ -0,0 +1,12 @@
+package cc.spray
+
+import json.formats.{JsonReader, JsonWriter}
+
+package object json {
+
+ def jsonReader[T](implicit reader: JsonReader[T]) = reader
+ def jsonWriter[T](implicit writer: JsonWriter[T]) = writer
+
+ implicit def pimpAny[T :JsonWriter](any: T): PimpedAny[T] = new PimpedAny(any, jsonWriter)
+
+} \ No newline at end of file