-
Notifications
You must be signed in to change notification settings - Fork 190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alternative product formats #93
base: master
Are you sure you want to change the base?
Changes from all commits
c40e5aa
f985f24
e273071
6934de8
12d83eb
37af0d4
f33e846
a6aaf51
dce0643
cddb9a2
493a11f
74f13db
ac5f073
373d5e6
f81c864
cc186e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
name := "spray-json" | ||
|
||
version := "1.3.0-SNAPSHOT" | ||
version := "1.3.0" | ||
|
||
organization := "io.spray" | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All these changes in here need to be performance verified. It would be easier if they'd be broken out into an extra PR. |
||
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 :_*)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add explicit return types to each of these (check with mima that they don't change). |
||
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] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a public method so it needs a proper deprecation cycle. |
||
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) | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be a binary compatibility problem? If yes, needs deprecated forwarder. |
||
} | ||
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) | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,7 +20,6 @@ import java.lang.{StringBuilder => JStringBuilder} | |
import java.nio.{CharBuffer, ByteBuffer} | ||
import java.nio.charset.Charset | ||
import scala.annotation.{switch, tailrec} | ||
import scala.collection.immutable.ListMap | ||
|
||
/** | ||
* Fast, no-dependency parser for JSON as defined by http://tools.ietf.org/html/rfc4627. | ||
|
@@ -29,7 +28,7 @@ object JsonParser { | |
def apply(input: ParserInput): JsValue = new JsonParser(input).parseJsValue() | ||
|
||
class ParsingException(val summary: String, val detail: String = "") | ||
extends RuntimeException(if (summary.isEmpty) detail else if (detail.isEmpty) summary else summary + ": " + detail) | ||
extends RuntimeException(if (summary.isEmpty) detail else if (detail.isEmpty) summary else summary + ":" + detail) | ||
} | ||
|
||
class JsonParser(input: ParserInput) { | ||
|
@@ -72,7 +71,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] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will probably lead to incompatible semantic change. |
||
var map = Map.empty[String, JsValue] | ||
@tailrec def members(): Unit = { | ||
`string`() | ||
require(':') | ||
|
@@ -91,7 +90,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 | ||
|
@@ -192,7 +191,7 @@ class JsonParser(input: ParserInput) { | |
} | ||
val detail = { | ||
val sanitizedText = text.map(c ⇒ if (Character.isISOControl(c)) '?' else c) | ||
s"\n$sanitizedText\n${" " * col}^\n" | ||
s"\n$sanitizedText\n${" " * (col-1)}^\n" | ||
} | ||
throw new ParsingException(summary, detail) | ||
} | ||
|
@@ -236,11 +235,11 @@ object ParserInput { | |
@tailrec def rec(ix: Int, lineStartIx: Int, lineNr: Int): Line = | ||
nextUtf8Char() match { | ||
case '\n' if index > ix => sb.setLength(0); rec(ix + 1, ix + 1, lineNr + 1) | ||
case '\n' | EOI => Line(lineNr, index - lineStartIx, sb.toString) | ||
case '\n' | EOI => Line(lineNr, index - lineStartIx + 1, sb.toString) | ||
case c => sb.append(c); rec(ix + 1, lineStartIx, lineNr) | ||
} | ||
val savedCursor = _cursor | ||
_cursor = 0 | ||
_cursor = -1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that a bug fix? |
||
val line = rec(ix = 0, lineStartIx = 0, lineNr = 1) | ||
_cursor = savedCursor | ||
line | ||
|
@@ -250,7 +249,7 @@ 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 represenation | ||
* 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 DefaultParserInput { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ | |
package spray.json | ||
|
||
import annotation.tailrec | ||
import java.lang.StringBuilder | ||
import java.lang.{StringBuilder => JStringBuilder} | ||
|
||
/** | ||
* A JsonPrinter serializes a JSON AST to a String. | ||
|
@@ -26,24 +26,22 @@ 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 | ||
def apply(x: JsValue, | ||
jsonpCallback: Option[String] = None, | ||
sb: JStringBuilder = new JStringBuilder(256)): String = { | ||
jsonpCallback match { | ||
case Some(callback) => { | ||
case Some(callback) => | ||
sb.append(callback).append('(') | ||
print(x, sb) | ||
sb.append(')'); | ||
} | ||
sb.append(')') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
case None => print(x, sb) | ||
} | ||
sb.toString | ||
} | ||
|
||
def print(x: JsValue, sb: StringBuilder) | ||
def print(x: JsValue, sb: JStringBuilder) | ||
|
||
protected def printLeaf(x: JsValue, sb: StringBuilder) { | ||
protected def printLeaf(x: JsValue, sb: JStringBuilder) { | ||
x match { | ||
case JsNull => sb.append("null") | ||
case JsTrue => sb.append("true") | ||
|
@@ -54,7 +52,7 @@ trait JsonPrinter extends (JsValue => String) { | |
} | ||
} | ||
|
||
protected def printString(s: String, sb: StringBuilder) { | ||
protected def printString(s: String, sb: JStringBuilder) { | ||
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) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove patch versions as they shouldn't matter (i.e. "2.10.x" and "2.11.x" and "2.12.x" and "2.13.0-M2"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed that in #239