summaryrefslogtreecommitdiff
path: root/src/main/scala/spray
diff options
context:
space:
mode:
authorMathias <mathias@spray.cc>2012-10-12 14:15:12 +0200
committerMathias <mathias@spray.cc>2012-10-12 14:15:12 +0200
commite5a7de26dfc8a4bb4410b7ee500624e30bf66650 (patch)
tree70d2133716f04df15b93350a36b8dd1aad28704c /src/main/scala/spray
parent5354b7b2b1af66049328eed150e036a314878559 (diff)
downloadspray-json-e5a7de26dfc8a4bb4410b7ee500624e30bf66650.tar.gz
spray-json-e5a7de26dfc8a4bb4410b7ee500624e30bf66650.tar.bz2
spray-json-e5a7de26dfc8a4bb4410b7ee500624e30bf66650.zip
Remove obsolete /cc/ package directories
Diffstat (limited to 'src/main/scala/spray')
-rw-r--r--src/main/scala/spray/json/AdditionalFormats.scala104
-rw-r--r--src/main/scala/spray/json/BasicFormats.scala129
-rw-r--r--src/main/scala/spray/json/CollectionFormats.scala94
-rw-r--r--src/main/scala/spray/json/CompactPrinter.scala51
-rw-r--r--src/main/scala/spray/json/DefaultJsonProtocol.scala30
-rw-r--r--src/main/scala/spray/json/JsValue.scala114
-rw-r--r--src/main/scala/spray/json/JsonFormat.scala71
-rw-r--r--src/main/scala/spray/json/JsonParser.scala121
-rw-r--r--src/main/scala/spray/json/JsonPrinter.scala89
-rw-r--r--src/main/scala/spray/json/PrettyPrinter.scala68
-rw-r--r--src/main/scala/spray/json/ProductFormats.scala530
-rw-r--r--src/main/scala/spray/json/StandardFormats.scala118
-rw-r--r--src/main/scala/spray/json/package.scala45
13 files changed, 1564 insertions, 0 deletions
diff --git a/src/main/scala/spray/json/AdditionalFormats.scala b/src/main/scala/spray/json/AdditionalFormats.scala
new file mode 100644
index 0000000..20fdc74
--- /dev/null
+++ b/src/main/scala/spray/json/AdditionalFormats.scala
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+package spray.json
+
+/**
+ * Provides additional JsonFormats and helpers
+ */
+trait AdditionalFormats {
+
+ implicit object JsValueFormat extends JsonFormat[JsValue] {
+ def write(value: JsValue) = value
+ def read(value: JsValue) = value
+ }
+
+ /**
+ * Constructs a JsonFormat from its two parts, JsonReader and JsonWriter.
+ */
+ def jsonFormat[T](reader: JsonReader[T], writer: JsonWriter[T]) = new JsonFormat[T] {
+ def write(obj: T) = writer.write(obj)
+ def read(json: JsValue) = reader.read(json)
+ }
+
+ /**
+ * Constructs a RootJsonFormat from its two parts, RootJsonReader and RootJsonWriter.
+ */
+ def rootJsonFormat[T](reader: RootJsonReader[T], writer: RootJsonWriter[T]) =
+ rootFormat(jsonFormat(reader, writer))
+
+ /**
+ * Turns a JsonWriter into a JsonFormat that throws an UnsupportedOperationException for reads.
+ */
+ def lift[T](writer :JsonWriter[T]) = new JsonFormat[T] {
+ def write(obj: T): JsValue = writer.write(obj)
+ def read(value: JsValue) =
+ throw new UnsupportedOperationException("JsonReader implementation missing")
+ }
+
+ /**
+ * Turns a RootJsonWriter into a RootJsonFormat that throws an UnsupportedOperationException for reads.
+ */
+ def lift[T](writer :RootJsonWriter[T]): RootJsonFormat[T] =
+ rootFormat(lift(writer :JsonWriter[T]))
+
+ /**
+ * Turns a JsonReader into a JsonFormat that throws an UnsupportedOperationException for writes.
+ */
+ def lift[T <: AnyRef](reader :JsonReader[T]) = new JsonFormat[T] {
+ def write(obj: T): JsValue =
+ throw new UnsupportedOperationException("No JsonWriter[" + obj.getClass + "] available")
+ def read(value: JsValue) = reader.read(value)
+ }
+
+ /**
+ * Turns a RootJsonReader into a RootJsonFormat that throws an UnsupportedOperationException for writes.
+ */
+ def lift[T <: AnyRef](reader :RootJsonReader[T]): RootJsonFormat[T] =
+ rootFormat(lift(reader :JsonReader[T]))
+
+ /**
+ * Lazy wrapper around serialization. Useful when you want to serialize (mutually) recursive structures.
+ */
+ def lazyFormat[T](format: => JsonFormat[T]) = new JsonFormat[T] {
+ lazy val delegate = format;
+ def write(x: T) = delegate.write(x);
+ def read(value: JsValue) = delegate.read(value);
+ }
+
+ /**
+ * Explicitly turns a JsonFormat into a RootJsonFormat.
+ */
+ def rootFormat[T](format: JsonFormat[T]) = new RootJsonFormat[T] {
+ def write(obj: T) = format.write(obj)
+ def read(json: JsValue) = format.read(json)
+ }
+
+ /**
+ * Wraps an existing JsonReader with Exception protection.
+ */
+ def safeReader[A :JsonReader] = new JsonReader[Either[Exception, A]] {
+ def read(json: JsValue) = {
+ try {
+ Right(json.convertTo)
+ } catch {
+ case e: Exception => Left(e)
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/scala/spray/json/BasicFormats.scala b/src/main/scala/spray/json/BasicFormats.scala
new file mode 100644
index 0000000..55fe0bf
--- /dev/null
+++ b/src/main/scala/spray/json/BasicFormats.scala
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+package spray.json
+
+/**
+ * Provides the JsonFormats for the most important Scala types.
+ */
+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 x => deserializationError("Expected Int as JsNumber, but got " + x)
+ }
+ }
+
+ implicit object LongJsonFormat extends JsonFormat[Long] {
+ def write(x: Long) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.longValue
+ case x => deserializationError("Expected Long as JsNumber, but got " + x)
+ }
+ }
+
+ implicit object FloatJsonFormat extends JsonFormat[Float] {
+ def write(x: Float) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.floatValue
+ case JsNull => Float.NaN
+ case x => deserializationError("Expected Float as JsNumber, but got " + x)
+ }
+ }
+
+ implicit object DoubleJsonFormat extends JsonFormat[Double] {
+ def write(x: Double) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.doubleValue
+ case JsNull => Double.NaN
+ case x => deserializationError("Expected Double as JsNumber, but got " + x)
+ }
+ }
+
+ implicit object ByteJsonFormat extends JsonFormat[Byte] {
+ def write(x: Byte) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.byteValue
+ case x => deserializationError("Expected Byte as JsNumber, but got " + x)
+ }
+ }
+
+ implicit object ShortJsonFormat extends JsonFormat[Short] {
+ def write(x: Short) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.shortValue
+ case x => deserializationError("Expected Short as JsNumber, but got " + x)
+ }
+ }
+
+ implicit object BigDecimalJsonFormat extends JsonFormat[BigDecimal] {
+ def write(x: BigDecimal) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x
+ case x => deserializationError("Expected BigDecimal as JsNumber, but got " + x)
+ }
+ }
+
+ implicit object BigIntJsonFormat extends JsonFormat[BigInt] {
+ def write(x: BigInt) = JsNumber(x)
+ def read(value: JsValue) = value match {
+ case JsNumber(x) => x.toBigInt
+ case x => deserializationError("Expected BigInt as JsNumber, but got " + x)
+ }
+ }
+
+ 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 x => deserializationError("Expected JsBoolean, but got " + x)
+ }
+ }
+
+ 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 x => deserializationError("Expected Char as single-character JsString, but got " + x)
+ }
+ }
+
+ implicit object StringJsonFormat extends JsonFormat[String] {
+ def write(x: String) = JsString(x)
+ def read(value: JsValue) = value match {
+ case JsString(x) => x
+ case x => deserializationError("Expected String as JsString, but got " + x)
+ }
+ }
+
+ 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 x => deserializationError("Expected Symbol as JsString, but got " + x)
+ }
+ }
+
+}
diff --git a/src/main/scala/spray/json/CollectionFormats.scala b/src/main/scala/spray/json/CollectionFormats.scala
new file mode 100644
index 0000000..3b0d0c2
--- /dev/null
+++ b/src/main/scala/spray/json/CollectionFormats.scala
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+package spray.json
+
+trait CollectionFormats {
+
+ /**
+ * Supplies the JsonFormat for Lists.
+ */
+ implicit def listFormat[T :JsonFormat] = new RootJsonFormat[List[T]] {
+ def write(list: List[T]) = JsArray(list.map(_.toJson))
+ def read(value: JsValue) = value match {
+ case JsArray(elements) => elements.map(_.convertTo[T])
+ case x => deserializationError("Expected List as JsArray, but got " + x)
+ }
+ }
+
+ /**
+ * Supplies the JsonFormat for Arrays.
+ */
+ implicit def arrayFormat[T :JsonFormat :ClassManifest] = new RootJsonFormat[Array[T]] {
+ def write(array: Array[T]) = JsArray(array.map(_.toJson).toList)
+ def read(value: JsValue) = value match {
+ case JsArray(elements) => elements.map(_.convertTo[T]).toArray[T]
+ case x => deserializationError("Expected Array as JsArray, but got " + x)
+ }
+ }
+
+ /**
+ * Supplies the JsonFormat for Maps. The implicitly available JsonFormat for the key type K must
+ * always write JsStrings, otherwise a [[spray.json.SerializationException]] will be thrown.
+ */
+ implicit def mapFormat[K :JsonFormat, V :JsonFormat] = new RootJsonFormat[Map[K, V]] {
+ def write(m: Map[K, V]) = JsObject {
+ m.map { field =>
+ field._1.toJson match {
+ case JsString(x) => x -> field._2.toJson
+ case x => throw new SerializationException("Map key must be formatted as JsString, not '" + x + "'")
+ }
+ }
+ }
+ def read(value: JsValue) = value match {
+ case x: JsObject => x.fields.map { field =>
+ (JsString(field._1).convertTo[K], field._2.convertTo[V])
+ } (collection.breakOut)
+ case x => deserializationError("Expected Map as JsObject, but got " + x)
+ }
+ }
+
+ import collection.{immutable => imm}
+
+ implicit def immIterableFormat[T :JsonFormat] = viaList[imm.Iterable[T], T](list => imm.Iterable(list :_*))
+ implicit def immSeqFormat[T :JsonFormat] = viaList[imm.Seq[T], T](list => imm.Seq(list :_*))
+ implicit def immIndexedSeqFormat[T :JsonFormat] = viaList[imm.IndexedSeq[T], T](list => imm.IndexedSeq(list :_*))
+ implicit def immLinearSeqFormat[T :JsonFormat] = viaList[imm.LinearSeq[T], T](list => imm.LinearSeq(list :_*))
+ implicit def immSetFormat[T :JsonFormat] = viaList[imm.Set[T], T](list => imm.Set(list :_*))
+ implicit def vectorFormat[T :JsonFormat] = viaList[Vector[T], T](list => Vector(list :_*))
+
+ import collection._
+
+ implicit def iterableFormat[T :JsonFormat] = viaList[Iterable[T], T](list => Iterable(list :_*))
+ implicit def seqFormat[T :JsonFormat] = viaList[Seq[T], T](list => Seq(list :_*))
+ implicit def indexedSeqFormat[T :JsonFormat] = viaList[IndexedSeq[T], T](list => IndexedSeq(list :_*))
+ implicit def linearSeqFormat[T :JsonFormat] = viaList[LinearSeq[T], T](list => LinearSeq(list :_*))
+ implicit def setFormat[T :JsonFormat] = viaList[Set[T], T](list => Set(list :_*))
+
+ /**
+ * A JsonFormat construction helper that creates a JsonFormat for an Iterable type I from a builder function
+ * List => I.
+ */
+ def viaList[I <: Iterable[T], T :JsonFormat](f: List[T] => I): RootJsonFormat[I] = new RootJsonFormat[I] {
+ def write(iterable: I) = JsArray(iterable.map(_.toJson).toList)
+ def read(value: JsValue) = value match {
+ case JsArray(elements) => f(elements.map(_.convertTo[T]))
+ case x => deserializationError("Expected Collection as JsArray, but got " + x)
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/scala/spray/json/CompactPrinter.scala b/src/main/scala/spray/json/CompactPrinter.scala
new file mode 100644
index 0000000..b7d2856
--- /dev/null
+++ b/src/main/scala/spray/json/CompactPrinter.scala
@@ -0,0 +1,51 @@
+/*
+ * 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 spray.json
+
+import java.lang.StringBuilder
+
+/**
+ * A JsonPrinter that produces compact JSON source without any superfluous whitespace.
+ */
+trait 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: Map[String, JsValue], sb: StringBuilder) {
+ sb.append('{')
+ printSeq(members, sb.append(',')) { m =>
+ printString(m._1, sb)
+ sb.append(':')
+ print(m._2, sb)
+ }
+ sb.append('}')
+ }
+
+ private def printArray(elements: List[JsValue], sb: StringBuilder) {
+ sb.append('[')
+ printSeq(elements, sb.append(','))(print(_, sb))
+ sb.append(']')
+ }
+}
+
+object CompactPrinter extends CompactPrinter \ No newline at end of file
diff --git a/src/main/scala/spray/json/DefaultJsonProtocol.scala b/src/main/scala/spray/json/DefaultJsonProtocol.scala
new file mode 100644
index 0000000..4c93184
--- /dev/null
+++ b/src/main/scala/spray/json/DefaultJsonProtocol.scala
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package spray.json
+
+/**
+ * Provides all the predefined JsonFormats.
+ */
+trait DefaultJsonProtocol
+ extends BasicFormats
+ with StandardFormats
+ with CollectionFormats
+ with ProductFormats
+ with AdditionalFormats
+
+object DefaultJsonProtocol extends DefaultJsonProtocol
diff --git a/src/main/scala/spray/json/JsValue.scala b/src/main/scala/spray/json/JsValue.scala
new file mode 100644
index 0000000..8b25c98
--- /dev/null
+++ b/src/main/scala/spray/json/JsValue.scala
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2009-2011 Mathias Doenitz
+ * Inspired by a similar implementation by Nathan Hamblen
+ * (https://github.com/n8han/Databinder-Dispatch)
+ *
+ * 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
+
+import collection.immutable.ListMap
+
+
+/**
+ * The general type of a JSON AST node.
+ */
+sealed abstract class JsValue {
+ override def toString = compactPrint
+ def toString(printer: (JsValue => String)) = printer(this)
+ def compactPrint = CompactPrinter(this)
+ def prettyPrint = PrettyPrinter(this)
+ def convertTo[T :JsonReader]: T = jsonReader[T].read(this)
+
+ /**
+ * Returns `this` if this JsValue is a JsObject, otherwise throws a DeserializationException with the given error msg.
+ */
+ def asJsObject(errorMsg: String = "JSON object expected"): JsObject = deserializationError(errorMsg)
+
+ /**
+ * Returns `this` if this JsValue is a JsObject, otherwise throws a DeserializationException.
+ */
+ def asJsObject: JsObject = asJsObject()
+
+ @deprecated("Superceded by 'convertTo'", "1.1.0")
+ def fromJson[T :JsonReader]: T = convertTo
+}
+
+/**
+ * A JSON object.
+ */
+case class JsObject(fields: Map[String, JsValue]) extends JsValue {
+ override def asJsObject(errorMsg: String) = this
+ def getFields(fieldNames: String*): Seq[JsValue] = fieldNames.flatMap(fields.get)
+}
+object JsObject {
+ // we use a ListMap in order to preserve the field order
+ def apply(members: JsField*) = new JsObject(ListMap(members: _*))
+ def apply(members: List[JsField]) = new JsObject(ListMap(members: _*))
+}
+
+/**
+ * A JSON array.
+ */
+case class JsArray(elements: List[JsValue]) extends JsValue
+object JsArray {
+ def apply(elements: JsValue*) = new JsArray(elements.toList)
+}
+
+/**
+ * A JSON string.
+ */
+case class JsString(value: String) extends JsValue
+
+object JsString {
+ def apply(value: Symbol) = new JsString(value.name)
+}
+
+/**
+ * A JSON number.
+ */
+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) = n match {
+ case n if n.isNaN => JsNull
+ case n if n.isInfinity => JsNull
+ case _ => new JsNumber(BigDecimal(n))
+ }
+ def apply(n: BigInt) = new JsNumber(BigDecimal(n))
+ def apply(n: String) = new JsNumber(BigDecimal(n))
+}
+
+/**
+ * JSON Booleans.
+ */
+sealed abstract class 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
+}
+
+/**
+ * The representation for JSON null.
+ */
+case object JsNull extends JsValue
diff --git a/src/main/scala/spray/json/JsonFormat.scala b/src/main/scala/spray/json/JsonFormat.scala
new file mode 100644
index 0000000..c4915cc
--- /dev/null
+++ b/src/main/scala/spray/json/JsonFormat.scala
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package spray.json
+
+import annotation.implicitNotFound
+
+/**
+ * Provides the JSON deserialization for type T.
+ */
+@implicitNotFound(msg = "Cannot find JsonReader or JsonFormat type class for ${T}")
+trait JsonReader[T] {
+ def read(json: JsValue): T
+}
+
+object JsonReader {
+ implicit def func2Reader[T](f: JsValue => T): JsonReader[T] = new JsonReader[T] {
+ def read(json: JsValue) = f(json)
+ }
+}
+
+/**
+ * Provides the JSON serialization for type T.
+ */
+@implicitNotFound(msg = "Cannot find JsonWriter or JsonFormat type class for ${T}")
+trait JsonWriter[T] {
+ def write(obj: T): JsValue
+}
+
+object JsonWriter {
+ implicit def func2Writer[T](f: T => JsValue): JsonWriter[T] = new JsonWriter[T] {
+ def write(obj: T) = f(obj)
+ }
+}
+
+/**
+ * Provides the JSON deserialization and serialization for type T.
+ */
+trait JsonFormat[T] extends JsonReader[T] with JsonWriter[T]
+
+/**
+ * A special JsonReader capable of reading a legal JSON root object, i.e. either a JSON array or a JSON object.
+ */
+@implicitNotFound(msg = "Cannot find RootJsonReader or RootJsonFormat type class for ${T}")
+trait RootJsonReader[T] extends JsonReader[T]
+
+/**
+ * A special JsonWriter capable of writing a legal JSON root object, i.e. either a JSON array or a JSON object.
+ */
+@implicitNotFound(msg = "Cannot find RootJsonWriter or RootJsonFormat type class for ${T}")
+trait RootJsonWriter[T] extends JsonWriter[T]
+
+/**
+ * A special JsonFormat signaling that the format produces a legal JSON root object, i.e. either a JSON array
+ * or a JSON object.
+ */
+trait RootJsonFormat[T] extends JsonFormat[T] with RootJsonReader[T] with RootJsonWriter[T] \ No newline at end of file
diff --git a/src/main/scala/spray/json/JsonParser.scala b/src/main/scala/spray/json/JsonParser.scala
new file mode 100644
index 0000000..c01193b
--- /dev/null
+++ b/src/main/scala/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 spray.json
+
+import org.parboiled.scala._
+import org.parboiled.errors.{ErrorUtils, ParsingException}
+import org.parboiled.Context
+import java.lang.StringBuilder
+
+/**
+ * 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
+ lazy val Json = rule { WhiteSpace ~ Value ~ EOI }
+
+ def JsonObject: Rule1[JsObject] = rule {
+ "{ " ~ zeroOrMore(Pair, separator = ", ") ~ "} " ~~> (JsObject(_ :_*))
+ }
+
+ def Pair = rule { JsonStringUnwrapped ~ ": " ~ Value ~~> ((_, _)) }
+
+ 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 technique that is a bit more verbose (and somewhat
+ // 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/spray/json/JsonPrinter.scala b/src/main/scala/spray/json/JsonPrinter.scala
new file mode 100644
index 0000000..23529f3
--- /dev/null
+++ b/src/main/scala/spray/json/JsonPrinter.scala
@@ -0,0 +1,89 @@
+/*
+ * 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 spray.json
+
+import annotation.tailrec
+import java.lang.StringBuilder
+
+/**
+ * A JsonPrinter serializes a JSON AST to a String.
+ */
+trait JsonPrinter extends (JsValue => String) {
+
+ def apply(x: JsValue): String = apply(x, None)
+
+ def apply(x: JsValue, jsonpCallback: String): String = apply(x, Some(jsonpCallback))
+
+ def apply(x: JsValue, jsonpCallback: Option[String]): String = {
+ val sb = new StringBuilder
+ jsonpCallback match {
+ case Some(callback) => {
+ sb.append(callback).append('(')
+ print(x, sb)
+ sb.append(')');
+ }
+ case None => 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/spray/json/PrettyPrinter.scala b/src/main/scala/spray/json/PrettyPrinter.scala
new file mode 100644
index 0000000..0daaf71
--- /dev/null
+++ b/src/main/scala/spray/json/PrettyPrinter.scala
@@ -0,0 +1,68 @@
+/*
+ * 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 spray.json
+
+import java.lang.StringBuilder
+import annotation.tailrec
+
+/**
+ * A JsonPrinter that produces a nicely readable JSON source.
+ */
+trait 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: Map[String, JsValue], sb: StringBuilder, indent: Int) {
+ sb.append("{\n")
+ printSeq(members, sb.append(",\n")) { m =>
+ printIndent(sb, indent + Indent)
+ printString(m._1, sb)
+ sb.append(": ")
+ print(m._2, 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)
+ }
+ }
+}
+
+object PrettyPrinter extends PrettyPrinter \ No newline at end of file
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
+ }
+}
diff --git a/src/main/scala/spray/json/StandardFormats.scala b/src/main/scala/spray/json/StandardFormats.scala
new file mode 100644
index 0000000..6a9712a
--- /dev/null
+++ b/src/main/scala/spray/json/StandardFormats.scala
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+package spray.json
+
+import scala.{Left, Right}
+
+/**
+ * Provides the JsonFormats for the non-collection standard types.
+ */
+trait StandardFormats {
+ this: AdditionalFormats =>
+
+ private[json] type JF[T] = JsonFormat[T] // simple alias for reduced verbosity
+
+ implicit def optionFormat[T :JF] = new OptionFormat[T]
+
+ class OptionFormat[T :JF] extends JF[Option[T]] {
+ def write(option: Option[T]) = option match {
+ case Some(x) => x.toJson
+ case None => JsNull
+ }
+ def read(value: JsValue) = value match {
+ case JsNull => None
+ case x => Some(x.convertTo[T])
+ }
+ }
+
+ implicit def eitherFormat[A :JF, B :JF] = new JF[Either[A, B]] {
+ def write(either: Either[A, B]) = either match {
+ case Right(a) => a.toJson
+ case Left(b) => b.toJson
+ }
+ def read(value: JsValue) = (value.convertTo(safeReader[A]), value.convertTo(safeReader[B])) match {
+ case (Right(a), _: Left[_, _]) => Left(a)
+ case (_: Left[_, _], Right(b)) => Right(b)
+ case (_: Right[_, _], _: Right[_, _]) => deserializationError("Ambiguous Either value: can be read as both, Left and Right, values")
+ case (Left(ea), Left(eb)) => deserializationError("Could not read Either value:\n" + ea + "---------- and ----------\n" + eb)
+ }
+ }
+
+ implicit def tuple1Format[A :JF] = new JF[Tuple1[A]] {
+ def write(t: Tuple1[A]) = t._1.toJson
+ def read(value: JsValue) = Tuple1(value.convertTo[A])
+ }
+
+ implicit def tuple2Format[A :JF, B :JF] = new RootJsonFormat[(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.convertTo[A], b.convertTo[B])
+ case x => deserializationError("Expected Tuple2 as JsArray, but got " + x)
+ }
+ }
+
+ implicit def tuple3Format[A :JF, B :JF, C :JF] = new RootJsonFormat[(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.convertTo[A], b.convertTo[B], c.convertTo[C])
+ case x => deserializationError("Expected Tuple3 as JsArray, but got " + x)
+ }
+ }
+
+ implicit def tuple4Format[A :JF, B :JF, C :JF, D :JF] = new RootJsonFormat[(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.convertTo[A], b.convertTo[B], c.convertTo[C], d.convertTo[D])
+ case x => deserializationError("Expected Tuple4 as JsArray, but got " + x)
+ }
+ }
+
+ implicit def tuple5Format[A :JF, B :JF, C :JF, D :JF, E :JF] = {
+ new RootJsonFormat[(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.convertTo[A], b.convertTo[B], c.convertTo[C], d.convertTo[D], e.convertTo[E])
+ case x => deserializationError("Expected Tuple5 as JsArray, but got " + x)
+ }
+ }
+ }
+
+ implicit def tuple6Format[A :JF, B :JF, C :JF, D :JF, E :JF, F: JF] = {
+ new RootJsonFormat[(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.convertTo[A], b.convertTo[B], c.convertTo[C], d.convertTo[D], e.convertTo[E], f.convertTo[F])
+ case x => deserializationError("Expected Tuple6 as JsArray, but got " + x)
+ }
+ }
+ }
+
+ implicit def tuple7Format[A :JF, B :JF, C :JF, D :JF, E :JF, F: JF, G: JF] = {
+ new RootJsonFormat[(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.convertTo[A], b.convertTo[B], c.convertTo[C], d.convertTo[D], e.convertTo[E], f.convertTo[F], g.convertTo[G])
+ case x => deserializationError("Expected Tuple7 as JsArray, but got " + x)
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/scala/spray/json/package.scala b/src/main/scala/spray/json/package.scala
new file mode 100644
index 0000000..5bf5714
--- /dev/null
+++ b/src/main/scala/spray/json/package.scala
@@ -0,0 +1,45 @@
+/*
+ * 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 spray
+
+package object json {
+
+ type JsField = (String, JsValue)
+
+ def deserializationError(msg: String, cause: Throwable = null) = throw new DeserializationException(msg, cause)
+ def serializationError(msg: String) = throw new SerializationException(msg)
+
+ def jsonReader[T](implicit reader: JsonReader[T]) = reader
+ def jsonWriter[T](implicit writer: JsonWriter[T]) = writer
+
+ implicit def pimpAny[T](any: T) = new PimpedAny(any)
+ implicit def pimpString(string: String) = new PimpedString(string)
+}
+
+package json {
+
+ class DeserializationException(msg: String, cause: Throwable = null) extends RuntimeException(msg, cause)
+ class SerializationException(msg: String) extends RuntimeException(msg)
+
+ private[json] class PimpedAny[T](any: T) {
+ def toJson(implicit writer: JsonWriter[T]): JsValue = writer.write(any)
+ }
+
+ private[json] class PimpedString(string: String) {
+ def asJson: JsValue = JsonParser(string)
+ }
+} \ No newline at end of file