diff --git a/src/main/scala/spray/json/CollectionFormats.scala b/src/main/scala/spray/json/CollectionFormats.scala index 3b0d0c26..24ab1dd0 100644 --- a/src/main/scala/spray/json/CollectionFormats.scala +++ b/src/main/scala/spray/json/CollectionFormats.scala @@ -23,9 +23,9 @@ 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]) + def write(list: List[T]) = JsArray(list.map(_.toJson).toVector) + def read(value: JsValue): List[T] = value match { + case JsArray(elements) => elements.map(_.convertTo[T])(collection.breakOut) case x => deserializationError("Expected List as JsArray, but got " + x) } } @@ -34,7 +34,7 @@ trait CollectionFormats { * 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 write(array: Array[T]) = JsArray(array.map(_.toJson).toVector) 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) @@ -64,31 +64,30 @@ trait CollectionFormats { 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 :_*)) + implicit def immIterableFormat[T :JsonFormat] = viaSeq[imm.Iterable[T], T](seq => imm.Iterable(seq :_*)) + implicit def immSeqFormat[T :JsonFormat] = viaSeq[imm.Seq[T], T](seq => imm.Seq(seq :_*)) + implicit def immIndexedSeqFormat[T :JsonFormat] = viaSeq[imm.IndexedSeq[T], T](seq => imm.IndexedSeq(seq :_*)) + implicit def immLinearSeqFormat[T :JsonFormat] = viaSeq[imm.LinearSeq[T], T](seq => imm.LinearSeq(seq :_*)) + implicit def immSetFormat[T :JsonFormat] = viaSeq[imm.Set[T], T](seq => imm.Set(seq :_*)) + implicit def vectorFormat[T :JsonFormat] = viaSeq[Vector[T], T](seq => Vector(seq :_*)) 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 :_*)) + implicit def iterableFormat[T :JsonFormat] = viaSeq[Iterable[T], T](seq => Iterable(seq :_*)) + implicit def seqFormat[T :JsonFormat] = viaSeq[Seq[T], T](seq => Seq(seq :_*)) + implicit def indexedSeqFormat[T :JsonFormat] = viaSeq[IndexedSeq[T], T](seq => IndexedSeq(seq :_*)) + implicit def linearSeqFormat[T :JsonFormat] = viaSeq[LinearSeq[T], T](seq => LinearSeq(seq :_*)) + implicit def setFormat[T :JsonFormat] = viaSeq[Set[T], T](seq => Set(seq :_*)) /** * 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 viaSeq[I <: Iterable[T], T :JsonFormat](f: imm.Seq[T] => I): RootJsonFormat[I] = new RootJsonFormat[I] { + def write(iterable: I) = JsArray(iterable.map(_.toJson).toVector) 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 index eca616f1..a51583d3 100644 --- a/src/main/scala/spray/json/CompactPrinter.scala +++ b/src/main/scala/spray/json/CompactPrinter.scala @@ -41,7 +41,7 @@ trait CompactPrinter extends JsonPrinter { sb.append('}') } - protected def printArray(elements: List[JsValue], sb: StringBuilder) { + protected def printArray(elements: Seq[JsValue], sb: StringBuilder) { sb.append('[') printSeq(elements, sb.append(','))(print(_, sb)) sb.append(']') diff --git a/src/main/scala/spray/json/JsValue.scala b/src/main/scala/spray/json/JsValue.scala index 7dfb06f7..bc2889d9 100644 --- a/src/main/scala/spray/json/JsValue.scala +++ b/src/main/scala/spray/json/JsValue.scala @@ -18,7 +18,7 @@ package spray.json -import collection.immutable.ListMap +import collection.immutable /** * The general type of a JSON AST node. @@ -49,20 +49,18 @@ sealed abstract class JsValue { */ 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) + def getFields(fieldNames: String*): immutable.Seq[JsValue] = fieldNames.flatMap(fields.get)(collection.breakOut) } 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: _*)) + def apply(members: JsField*) = new JsObject(Map(members: _*)) } /** * A JSON array. */ -case class JsArray(elements: List[JsValue]) extends JsValue +case class JsArray(elements: Vector[JsValue]) extends JsValue object JsArray { - def apply(elements: JsValue*) = new JsArray(elements.toList) + def apply(elements: JsValue*) = new JsArray(elements.toVector) } /** diff --git a/src/main/scala/spray/json/JsonParser.scala b/src/main/scala/spray/json/JsonParser.scala index 36aa3388..19718a7e 100644 --- a/src/main/scala/spray/json/JsonParser.scala +++ b/src/main/scala/spray/json/JsonParser.scala @@ -72,7 +72,7 @@ class JsonParser(input: ParserInput) { // http://tools.ietf.org/html/rfc4627#section-2.2 private def `object`(): Unit = { ws() - var map = ListMap.empty[String, JsValue] + var map = Map.empty[String, JsValue] @tailrec def members(): Unit = { `string`() require(':') @@ -91,7 +91,7 @@ class JsonParser(input: ParserInput) { // http://tools.ietf.org/html/rfc4627#section-2.3 private def `array`(): Unit = { ws() - var list = List.newBuilder[JsValue] + var list = Vector.newBuilder[JsValue] @tailrec def values(): Unit = { `value`() list += jsValue diff --git a/src/main/scala/spray/json/PrettyPrinter.scala b/src/main/scala/spray/json/PrettyPrinter.scala index 5a2f142b..57cf35e7 100644 --- a/src/main/scala/spray/json/PrettyPrinter.scala +++ b/src/main/scala/spray/json/PrettyPrinter.scala @@ -50,7 +50,7 @@ trait PrettyPrinter extends JsonPrinter { sb.append("}") } - protected def printArray(elements: List[JsValue], sb: StringBuilder, indent: Int) { + protected def printArray(elements: Seq[JsValue], sb: StringBuilder, indent: Int) { sb.append('[') printSeq(elements, sb.append(", "))(print(_, sb, indent)) sb.append(']') diff --git a/src/main/scala/spray/json/StandardFormats.scala b/src/main/scala/spray/json/StandardFormats.scala index d71dcae4..154077a5 100644 --- a/src/main/scala/spray/json/StandardFormats.scala +++ b/src/main/scala/spray/json/StandardFormats.scala @@ -61,7 +61,7 @@ trait StandardFormats { 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 JsArray(Seq(a, b)) => (a.convertTo[A], b.convertTo[B]) case x => deserializationError("Expected Tuple2 as JsArray, but got " + x) } } @@ -69,7 +69,7 @@ trait StandardFormats { 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 JsArray(Seq(a, b, c)) => (a.convertTo[A], b.convertTo[B], c.convertTo[C]) case x => deserializationError("Expected Tuple3 as JsArray, but got " + x) } } @@ -77,7 +77,7 @@ trait StandardFormats { 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 JsArray(Seq(a, b, c, d)) => (a.convertTo[A], b.convertTo[B], c.convertTo[C], d.convertTo[D]) case x => deserializationError("Expected Tuple4 as JsArray, but got " + x) } } @@ -86,7 +86,7 @@ trait StandardFormats { 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) => + case JsArray(Seq(a, b, c, d, e)) => (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) } @@ -97,7 +97,7 @@ trait StandardFormats { 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) => + case JsArray(Seq(a, b, c, d, e, f)) => (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) } @@ -108,7 +108,7 @@ trait StandardFormats { 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._7.toJson) def read(value: JsValue) = value match { - case JsArray(a :: b :: c :: d :: e :: f :: g :: Nil) => + case JsArray(Seq(a, b, c, d, e, f, g)) => (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) } diff --git a/src/test/scala/spray/json/PrettyPrinterSpec.scala b/src/test/scala/spray/json/PrettyPrinterSpec.scala index 27137a88..6354ef0b 100644 --- a/src/test/scala/spray/json/PrettyPrinterSpec.scala +++ b/src/test/scala/spray/json/PrettyPrinterSpec.scala @@ -16,49 +16,49 @@ package spray.json +import scala.collection.immutable.ListMap import org.specs2.mutable._ class PrettyPrinterSpec extends Specification { "The PrettyPrinter" should { "print a more complicated JsObject nicely aligned" in { - PrettyPrinter { - JsonParser { - """|{ - | "simpleKey" : "some value", - | "key with spaces": null, - | "zero": 0, - | "number": -1.2323424E-5, - | "Boolean yes":true, - | "Boolean no": false, - | "Unic\u00f8de" : "Long string with newline\nescape", - | "key with \"quotes\"" : "string", - | "sub object" : { - | "sub key": 26.5, - | "a": "b", - | "array": [1, 2, { "yes":1, "no":0 }, ["a", "b", null], false] - | } - |}""".stripMargin - } - } mustEqual { - """|{ - | "simpleKey": "some value", - | "key with spaces": null, - | "zero": 0, - | "number": -0.000012323424, - | "Boolean yes": true, - | "Boolean no": false, - | "Unic\u00f8de": "Long string with newline\nescape", - | "key with \"quotes\"": "string", - | "sub object": { - | "sub key": 26.5, - | "a": "b", - | "array": [1, 2, { - | "yes": 1, - | "no": 0 - | }, ["a", "b", null], false] - | } - |}""".stripMargin + val JsObject(fields) = JsonParser { + """{ + | "Boolean no": false, + | "Boolean yes":true, + | "Unic\u00f8de" : "Long string with newline\nescape", + | "key with \"quotes\"" : "string", + | "key with spaces": null, + | "number": -1.2323424E-5, + | "simpleKey" : "some value", + | "sub object" : { + | "sub key": 26.5, + | "a": "b", + | "array": [1, 2, { "yes":1, "no":0 }, ["a", "b", null], false] + | }, + | "zero": 0 + |}""".stripMargin + } + PrettyPrinter(JsObject(ListMap(fields.toSeq.sortBy(_._1):_*))) mustEqual { + """{ + | "Boolean no": false, + | "Boolean yes": true, + | "Unic\u00f8de": "Long string with newline\nescape", + | "key with \"quotes\"": "string", + | "key with spaces": null, + | "number": -0.000012323424, + | "simpleKey": "some value", + | "sub object": { + | "sub key": 26.5, + | "a": "b", + | "array": [1, 2, { + | "yes": 1, + | "no": 0 + | }, ["a", "b", null], false] + | }, + | "zero": 0 + |}""".stripMargin } } } diff --git a/src/test/scala/spray/json/ReadmeSpec.scala b/src/test/scala/spray/json/ReadmeSpec.scala index 51a1ec50..306b6568 100644 --- a/src/test/scala/spray/json/ReadmeSpec.scala +++ b/src/test/scala/spray/json/ReadmeSpec.scala @@ -60,7 +60,7 @@ class ReadmeSpec extends Specification { JsArray(JsString(c.name), JsNumber(c.red), JsNumber(c.green), JsNumber(c.blue)) def read(value: JsValue) = value match { - case JsArray(JsString(name) :: JsNumber(red) :: JsNumber(green) :: JsNumber(blue) :: Nil) => + case JsArray(Seq(JsString(name), JsNumber(red), JsNumber(green), JsNumber(blue))) => new Color(name, red.toInt, green.toInt, blue.toInt) case _ => deserializationError("Color expected") } diff --git a/src/test/scala/spray/json/RoundTripSpecs.scala b/src/test/scala/spray/json/RoundTripSpecs.scala index d9e16c10..7609a53b 100644 --- a/src/test/scala/spray/json/RoundTripSpecs.scala +++ b/src/test/scala/spray/json/RoundTripSpecs.scala @@ -20,7 +20,7 @@ object JsValueGenerators { for { n <- choose(0, 15) els <- Gen.containerOfN[List, JsValue](n, genValue(depth - 1)) - } yield JsArray(els) + } yield JsArray(els.toVector) def genField(depth: Int): Gen[(String, JsValue)] = for { key <- parseableString @@ -32,7 +32,7 @@ object JsValueGenerators { for { n <- choose(0, 15) fields <- Gen.containerOfN[List, (String, JsValue)](n, genField(depth - 1)) - } yield JsObject(fields) + } yield JsObject(fields: _*) def genValue(depth: Int): Gen[JsValue] = oneOf(