summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/spray/json/BasicFormats.scala2
-rw-r--r--src/main/scala/spray/json/CompactPrinter.scala6
-rw-r--r--src/main/scala/spray/json/JsValue.scala3
-rw-r--r--src/main/scala/spray/json/JsonParser.scala35
-rw-r--r--src/main/scala/spray/json/JsonPrinter.scala8
-rw-r--r--src/main/scala/spray/json/PrettyPrinter.scala14
-rw-r--r--src/main/scala/spray/json/SortedPrinter.scala25
-rw-r--r--src/main/scala/spray/json/package.scala24
-rw-r--r--src/test/scala/spray/json/BasicFormatsSpec.scala2
-rw-r--r--src/test/scala/spray/json/JsonParserSpec.scala18
-rw-r--r--src/test/scala/spray/json/RoundTripSpecs.scala16
-rw-r--r--src/test/scala/spray/json/SortedPrinterSpec.scala65
-rw-r--r--src/test/scala/spray/json/StandardFormatsSpec.scala12
13 files changed, 193 insertions, 37 deletions
diff --git a/src/main/scala/spray/json/BasicFormats.scala b/src/main/scala/spray/json/BasicFormats.scala
index 65b9ecb..2e6342f 100644
--- a/src/main/scala/spray/json/BasicFormats.scala
+++ b/src/main/scala/spray/json/BasicFormats.scala
@@ -98,7 +98,7 @@ trait BasicFormats {
implicit object UnitJsonFormat extends JsonFormat[Unit] {
def write(x: Unit) = JsNumber(1)
- def read(value: JsValue) {}
+ def read(value: JsValue): Unit = {}
}
implicit object BooleanJsonFormat extends JsonFormat[Boolean] {
diff --git a/src/main/scala/spray/json/CompactPrinter.scala b/src/main/scala/spray/json/CompactPrinter.scala
index a51583d..7260c2f 100644
--- a/src/main/scala/spray/json/CompactPrinter.scala
+++ b/src/main/scala/spray/json/CompactPrinter.scala
@@ -23,7 +23,7 @@ import java.lang.StringBuilder
*/
trait CompactPrinter extends JsonPrinter {
- def print(x: JsValue, sb: StringBuilder) {
+ def print(x: JsValue, sb: StringBuilder): Unit = {
x match {
case JsObject(x) => printObject(x, sb)
case JsArray(x) => printArray(x, sb)
@@ -31,7 +31,7 @@ trait CompactPrinter extends JsonPrinter {
}
}
- protected def printObject(members: Map[String, JsValue], sb: StringBuilder) {
+ protected def printObject(members: Map[String, JsValue], sb: StringBuilder): Unit = {
sb.append('{')
printSeq(members, sb.append(',')) { m =>
printString(m._1, sb)
@@ -41,7 +41,7 @@ trait CompactPrinter extends JsonPrinter {
sb.append('}')
}
- protected def printArray(elements: Seq[JsValue], sb: StringBuilder) {
+ protected def printArray(elements: Seq[JsValue], sb: StringBuilder): Unit = {
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 b21672d..08a673b 100644
--- a/src/main/scala/spray/json/JsValue.scala
+++ b/src/main/scala/spray/json/JsValue.scala
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2009-2011 Mathias Doenitz
* Inspired by a similar implementation by Nathan Hamblen
- * (https://github.com/n8han/Databinder-Dispatch)
+ * (https://github.com/dispatch/classic)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ sealed abstract class JsValue {
def toString(printer: (JsValue => String)) = printer(this)
def compactPrint = CompactPrinter(this)
def prettyPrint = PrettyPrinter(this)
+ def sortedPrint = SortedPrinter(this)
def convertTo[T :JsonReader]: T = jsonReader[T].read(this)
/**
diff --git a/src/main/scala/spray/json/JsonParser.scala b/src/main/scala/spray/json/JsonParser.scala
index b1e59d5..71c4c11 100644
--- a/src/main/scala/spray/json/JsonParser.scala
+++ b/src/main/scala/spray/json/JsonParser.scala
@@ -38,10 +38,14 @@ class JsonParser(input: ParserInput) {
private[this] var cursorChar: Char = input.nextChar()
private[this] var jsValue: JsValue = _
- def parseJsValue(): JsValue = {
+ def parseJsValue(): JsValue =
+ parseJsValue(false)
+
+ def parseJsValue(allowTrailingInput: Boolean): JsValue = {
ws()
`value`()
- require(EOI)
+ if (!allowTrailingInput)
+ require(EOI)
jsValue
}
@@ -266,23 +270,25 @@ object ParserInput {
private val UTF8 = Charset.forName("UTF-8")
/**
- * ParserInput reading directly off a byte array which is assumed to contain the UTF-8 encoded representation
- * of the JSON input, without requiring a separate decoding step.
+ * ParserInput that allows to create a ParserInput from any randomly accessible indexed byte storage.
*/
- class ByteArrayBasedParserInput(bytes: Array[Byte]) extends DefaultParserInput {
+ abstract class IndexedBytesParserInput extends DefaultParserInput {
+ def length: Int
+ protected def byteAt(offset: Int): Byte
+
private val byteBuffer = ByteBuffer.allocate(4)
private val charBuffer = CharBuffer.allocate(2)
private val decoder = UTF8.newDecoder()
def nextChar() = {
_cursor += 1
- if (_cursor < bytes.length) (bytes(_cursor) & 0xFF).toChar else EOI
+ if (_cursor < length) (byteAt(_cursor) & 0xFF).toChar else EOI
}
def nextUtf8Char() = {
@tailrec def decode(byte: Byte, remainingBytes: Int): Char = {
byteBuffer.put(byte)
if (remainingBytes > 0) {
_cursor += 1
- if (_cursor < bytes.length) decode(bytes(_cursor), remainingBytes - 1) else ErrorChar
+ if (_cursor < length) decode(byteAt(_cursor), remainingBytes - 1) else ErrorChar
} else {
byteBuffer.flip()
val coderResult = decoder.decode(byteBuffer, charBuffer, false)
@@ -300,8 +306,8 @@ object ParserInput {
result
} else {
_cursor += 1
- if (_cursor < bytes.length) {
- val byte = bytes(_cursor)
+ if (_cursor < length) {
+ val byte = byteAt(_cursor)
if (byte >= 0) byte.toChar // 7-Bit ASCII
else if ((byte & 0xE0) == 0xC0) decode(byte, 1) // 2-byte UTF-8 sequence
else if ((byte & 0xF0) == 0xE0) decode(byte, 2) // 3-byte UTF-8 sequence
@@ -310,7 +316,16 @@ object ParserInput {
} else EOI
}
}
- def length = bytes.length
+ }
+
+ /**
+ * ParserInput reading directly off a byte array which is assumed to contain the UTF-8 encoded representation
+ * of the JSON input, without requiring a separate decoding step.
+ */
+ class ByteArrayBasedParserInput(bytes: Array[Byte]) extends IndexedBytesParserInput {
+ protected def byteAt(offset: Int): Byte = bytes(offset)
+ def length: Int = bytes.length
+
def sliceString(start: Int, end: Int) = new String(bytes, start, end - start, UTF8)
def sliceCharArray(start: Int, end: Int) =
UTF8.decode(ByteBuffer.wrap(java.util.Arrays.copyOfRange(bytes, start, end))).array()
diff --git a/src/main/scala/spray/json/JsonPrinter.scala b/src/main/scala/spray/json/JsonPrinter.scala
index 258fc5a..f132ab9 100644
--- a/src/main/scala/spray/json/JsonPrinter.scala
+++ b/src/main/scala/spray/json/JsonPrinter.scala
@@ -39,9 +39,9 @@ trait JsonPrinter extends (JsValue => String) {
sb.toString
}
- def print(x: JsValue, sb: JStringBuilder)
+ def print(x: JsValue, sb: JStringBuilder): Unit
- protected def printLeaf(x: JsValue, sb: JStringBuilder) {
+ protected def printLeaf(x: JsValue, sb: JStringBuilder): Unit = {
x match {
case JsNull => sb.append("null")
case JsTrue => sb.append("true")
@@ -52,7 +52,7 @@ trait JsonPrinter extends (JsValue => String) {
}
}
- protected def printString(s: String, sb: JStringBuilder) {
+ protected def printString(s: String, sb: JStringBuilder): Unit = {
import JsonPrinter._
@tailrec def firstToBeEncoded(ix: Int = 0): Int =
if (ix == s.length) -1 else if (requiresEncoding(s.charAt(ix))) ix else firstToBeEncoded(ix + 1)
@@ -85,7 +85,7 @@ trait JsonPrinter extends (JsValue => String) {
sb.append('"')
}
- protected def printSeq[A](iterable: Iterable[A], printSeparator: => Unit)(f: A => Unit) {
+ protected def printSeq[A](iterable: Iterable[A], printSeparator: => Unit)(f: A => Unit): Unit = {
var first = true
iterable.foreach { a =>
if (first) first = false else printSeparator
diff --git a/src/main/scala/spray/json/PrettyPrinter.scala b/src/main/scala/spray/json/PrettyPrinter.scala
index 57cf35e..7526dab 100644
--- a/src/main/scala/spray/json/PrettyPrinter.scala
+++ b/src/main/scala/spray/json/PrettyPrinter.scala
@@ -25,11 +25,11 @@ import annotation.tailrec
trait PrettyPrinter extends JsonPrinter {
val Indent = 2
- def print(x: JsValue, sb: StringBuilder) {
+ def print(x: JsValue, sb: StringBuilder): Unit = {
print(x, sb, 0)
}
- protected def print(x: JsValue, sb: StringBuilder, indent: Int) {
+ protected def print(x: JsValue, sb: StringBuilder, indent: Int): Unit = {
x match {
case JsObject(x) => printObject(x, sb, indent)
case JsArray(x) => printArray(x, sb, indent)
@@ -37,9 +37,11 @@ trait PrettyPrinter extends JsonPrinter {
}
}
- protected def printObject(members: Map[String, JsValue], sb: StringBuilder, indent: Int) {
+ protected def organiseMembers(members: Map[String, JsValue]): Seq[(String, JsValue)] = members.toSeq
+
+ protected def printObject(members: Map[String, JsValue], sb: StringBuilder, indent: Int): Unit = {
sb.append("{\n")
- printSeq(members, sb.append(",\n")) { m =>
+ printSeq(organiseMembers(members), sb.append(",\n")) { m =>
printIndent(sb, indent + Indent)
printString(m._1, sb)
sb.append(": ")
@@ -50,13 +52,13 @@ trait PrettyPrinter extends JsonPrinter {
sb.append("}")
}
- protected def printArray(elements: Seq[JsValue], sb: StringBuilder, indent: Int) {
+ protected def printArray(elements: Seq[JsValue], sb: StringBuilder, indent: Int): Unit = {
sb.append('[')
printSeq(elements, sb.append(", "))(print(_, sb, indent))
sb.append(']')
}
- protected def printIndent(sb: StringBuilder, indent: Int) {
+ protected def printIndent(sb: StringBuilder, indent: Int): Unit = {
@tailrec def rec(indent: Int): Unit =
if (indent > 0) {
sb.append(' ')
diff --git a/src/main/scala/spray/json/SortedPrinter.scala b/src/main/scala/spray/json/SortedPrinter.scala
new file mode 100644
index 0000000..28db225
--- /dev/null
+++ b/src/main/scala/spray/json/SortedPrinter.scala
@@ -0,0 +1,25 @@
+/*
+ * 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
+
+trait SortedPrinter extends PrettyPrinter {
+
+ override protected def organiseMembers(members: Map[String, JsValue]): Seq[(String, JsValue)] =
+ members.toSeq.sortBy(_._1)
+}
+
+object SortedPrinter extends SortedPrinter
diff --git a/src/main/scala/spray/json/package.scala b/src/main/scala/spray/json/package.scala
index f79b99e..37d63c2 100644
--- a/src/main/scala/spray/json/package.scala
+++ b/src/main/scala/spray/json/package.scala
@@ -25,9 +25,14 @@ package object json {
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)
+
+ implicit def enrichAny[T](any: T) = new RichAny(any)
+ implicit def enrichString(string: String) = new RichString(string)
+
+ @deprecated("use enrichAny", "1.3.4")
+ def pimpAny[T](any: T) = new PimpedAny(any)
+ @deprecated("use enrichString", "1.3.4")
+ def pimpString(string: String) = new PimpedString(string)
}
package json {
@@ -35,13 +40,26 @@ package json {
case class DeserializationException(msg: String, cause: Throwable = null, fieldNames: List[String] = Nil) extends RuntimeException(msg, cause)
class SerializationException(msg: String) extends RuntimeException(msg)
+ private[json] class RichAny[T](any: T) {
+ def toJson(implicit writer: JsonWriter[T]): JsValue = writer.write(any)
+ }
+
+ private[json] class RichString(string: String) {
+ @deprecated("deprecated in favor of parseJson", "1.2.6")
+ def asJson: JsValue = parseJson
+ def parseJson: JsValue = JsonParser(string)
+ }
+
+ @deprecated("use RichAny", "1.3.4")
private[json] class PimpedAny[T](any: T) {
def toJson(implicit writer: JsonWriter[T]): JsValue = writer.write(any)
}
+ @deprecated("use RichString", "1.3.4")
private[json] class PimpedString(string: String) {
@deprecated("deprecated in favor of parseJson", "1.2.6")
def asJson: JsValue = parseJson
def parseJson: JsValue = JsonParser(string)
}
+
}
diff --git a/src/test/scala/spray/json/BasicFormatsSpec.scala b/src/test/scala/spray/json/BasicFormatsSpec.scala
index 00da813..8417df2 100644
--- a/src/test/scala/spray/json/BasicFormatsSpec.scala
+++ b/src/test/scala/spray/json/BasicFormatsSpec.scala
@@ -127,7 +127,7 @@ class BasicFormatsSpec extends Specification with DefaultJsonProtocol {
().toJson mustEqual JsNumber(1)
}
"convert a JsNumber to Unit" in {
- JsNumber(1).convertTo[Unit] mustEqual ()
+ JsNumber(1).convertTo[Unit] mustEqual (())
}
}
diff --git a/src/test/scala/spray/json/JsonParserSpec.scala b/src/test/scala/spray/json/JsonParserSpec.scala
index 0f7ae7f..a97f021 100644
--- a/src/test/scala/spray/json/JsonParserSpec.scala
+++ b/src/test/scala/spray/json/JsonParserSpec.scala
@@ -74,7 +74,8 @@ class JsonParserSpec extends Specification {
}
"be reentrant" in {
val largeJsonSource = scala.io.Source.fromInputStream(getClass.getResourceAsStream("/test.json")).mkString
- List.fill(20)(largeJsonSource).par.map(JsonParser(_)).toList.map {
+ import scala.collection.parallel.immutable.ParSeq
+ ParSeq.fill(20)(largeJsonSource).map(JsonParser(_)).toList.map {
_.asInstanceOf[JsObject].fields("questions").asInstanceOf[JsArray].elements.size
} === List.fill(20)(100)
}
@@ -107,5 +108,18 @@ class JsonParserSpec extends Specification {
| ^
|""".stripMargin
}
+
+ "parse multiple values when allowTrailingInput" in {
+ val parser = new JsonParser("""{"key":1}{"key":2}""")
+ parser.parseJsValue(true) === JsObject("key" -> JsNumber(1))
+ parser.parseJsValue(true) === JsObject("key" -> JsNumber(2))
+ }
+ "reject trailing input when !allowTrailingInput" in {
+ def parser = JsonParser("""{"key":1}x""")
+ parser must throwA[JsonParser.ParsingException].like {
+ case e: JsonParser.ParsingException => e.getMessage must contain("expected end-of-input")
+ }
+ }
+
}
-} \ No newline at end of file
+}
diff --git a/src/test/scala/spray/json/RoundTripSpecs.scala b/src/test/scala/spray/json/RoundTripSpecs.scala
index 24b2354..6bee7b4 100644
--- a/src/test/scala/spray/json/RoundTripSpecs.scala
+++ b/src/test/scala/spray/json/RoundTripSpecs.scala
@@ -1,3 +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 spray.json
import org.specs2.mutable.Specification
diff --git a/src/test/scala/spray/json/SortedPrinterSpec.scala b/src/test/scala/spray/json/SortedPrinterSpec.scala
new file mode 100644
index 0000000..f91640e
--- /dev/null
+++ b/src/test/scala/spray/json/SortedPrinterSpec.scala
@@ -0,0 +1,65 @@
+/*
+ * 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
+
+import org.specs2.mutable._
+
+class SortedPrinterSpec extends Specification {
+
+ "The SortedPrinter" should {
+ "print a more complicated JsObject nicely aligned with fields sorted" in {
+ val obj = JsonParser {
+ """{
+ | "Unic\u00f8de" : "Long string with newline\nescape",
+ | "Boolean no": false,
+ | "number": -1.2323424E-5,
+ | "key with \"quotes\"" : "string",
+ | "key with spaces": null,
+ | "simpleKey" : "some value",
+ | "zero": 0,
+ | "sub object" : {
+ | "sub key": 26.5,
+ | "a": "b",
+ | "array": [1, 2, { "yes":1, "no":0 }, ["a", "b", null], false]
+ | },
+ | "Boolean yes":true
+ |}""".stripMargin
+ }
+ SortedPrinter(obj) 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": {
+ | "a": "b",
+ | "array": [1, 2, {
+ | "no": 0,
+ | "yes": 1
+ | }, ["a", "b", null], false],
+ | "sub key": 26.5
+ | },
+ | "zero": 0
+ |}""".stripMargin
+ }
+ }
+ }
+
+}
diff --git a/src/test/scala/spray/json/StandardFormatsSpec.scala b/src/test/scala/spray/json/StandardFormatsSpec.scala
index 89f01ac..833f06a 100644
--- a/src/test/scala/spray/json/StandardFormatsSpec.scala
+++ b/src/test/scala/spray/json/StandardFormatsSpec.scala
@@ -69,7 +69,7 @@ class StandardFormatsSpec extends Specification with DefaultJsonProtocol {
(42, 4.2).toJson mustEqual json
}
"be able to convert a JsArray to a (Int, Double)]" in {
- json.convertTo[(Int, Double)] mustEqual (42, 4.2)
+ json.convertTo[(Int, Double)] mustEqual ((42, 4.2))
}
}
@@ -79,7 +79,7 @@ class StandardFormatsSpec extends Specification with DefaultJsonProtocol {
(42, 4.2, 3).toJson mustEqual json
}
"be able to convert a JsArray to a (Int, Double, Int)]" in {
- json.convertTo[(Int, Double, Int)] mustEqual (42, 4.2, 3)
+ json.convertTo[(Int, Double, Int)] mustEqual ((42, 4.2, 3))
}
}
"The tuple4Format" should {
@@ -88,7 +88,7 @@ class StandardFormatsSpec extends Specification with DefaultJsonProtocol {
(42, 4.2, 3, 4).toJson mustEqual json
}
"be able to convert a JsArray to a (Int, Double, Int, Int)]" in {
- json.convertTo[(Int, Double, Int, Int)] mustEqual (42, 4.2, 3, 4)
+ json.convertTo[(Int, Double, Int, Int)] mustEqual ((42, 4.2, 3, 4))
}
}
"The tuple5Format" should {
@@ -97,7 +97,7 @@ class StandardFormatsSpec extends Specification with DefaultJsonProtocol {
(42, 4.2, 3, 4, 5).toJson mustEqual json
}
"be able to convert a JsArray to a (Int, Double, Int, Int, Int)]" in {
- json.convertTo[(Int, Double, Int, Int, Int)] mustEqual (42, 4.2, 3, 4, 5)
+ json.convertTo[(Int, Double, Int, Int, Int)] mustEqual ((42, 4.2, 3, 4, 5))
}
}
"The tuple6Format" should {
@@ -106,7 +106,7 @@ class StandardFormatsSpec extends Specification with DefaultJsonProtocol {
(42, 4.2, 3, 4, 5, 6).toJson mustEqual json
}
"be able to convert a JsArray to a (Int, Double, Int, Int, Int, Int)]" in {
- json.convertTo[(Int, Double, Int, Int, Int, Int)] mustEqual (42, 4.2, 3, 4, 5, 6)
+ json.convertTo[(Int, Double, Int, Int, Int, Int)] mustEqual ((42, 4.2, 3, 4, 5, 6))
}
}
"The tuple7Format" should {
@@ -115,7 +115,7 @@ class StandardFormatsSpec extends Specification with DefaultJsonProtocol {
(42, 4.2, 3, 4, 5, 6, 7).toJson mustEqual json
}
"be able to convert a JsArray to a (Int, Double, Int, Int, Int, Int, Int)]" in {
- json.convertTo[(Int, Double, Int, Int, Int, Int, Int)] mustEqual (42, 4.2, 3, 4, 5, 6, 7)
+ json.convertTo[(Int, Double, Int, Int, Int, Int, Int)] mustEqual ((42, 4.2, 3, 4, 5, 6, 7))
}
}
}