Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

adding support for mapping arrays/objects

  • Loading branch information...
commit 0becd6ce1e44c4431fc2feb7e9fa8f696d511600 1 parent affd513
mmcbride authored
View
107 src/main/scala/com/twitter/streamyj/Streamy.scala
@@ -78,12 +78,12 @@ class Streamy(parser: JsonParser, val source: String) {
/**
* alias for readObject(fn)
*/
- def \[T](fn: PartialFunction[String, T]) = readObject(fn)
+ def \(fn: ObjectParseFunc) = readObject(fn)
/**
* An alias for readArray.
*/
- def arr[T](fn: Function[Int, T]) = readArray(fn)
+ def arr(fn: ArrayParseFunc) = readArray(fn)
/**
* Matches the start of an object value, but without reading the object
@@ -116,7 +116,7 @@ class Streamy(parser: JsonParser, val source: String) {
* read the corresponding value or skip it. Not doing so will leave
* the parser in an unpredictable state.
*/
- def readObject[T](fn: PartialFunction[String, T]) = {
+ def readObject(fn: ObjectParseFunc) {
startObject()
readObjectBody(fn)
}
@@ -128,7 +128,7 @@ class Streamy(parser: JsonParser, val source: String) {
* read the corresponding value or skip it. Not doing so will leave
* the parser in an unpredictable state.
*/
- def readObjectOption[T](fn: PartialFunction[String, T]): Boolean = {
+ def readObjectOption(fn: ObjectParseFunc): Boolean = {
startObjectOption() && { readObjectBody(fn); true }
}
@@ -140,23 +140,65 @@ class Streamy(parser: JsonParser, val source: String) {
* read the corresponding value or skip it. Not doing so will leave
* the parser in an unpredictable state.
*/
- def readObjectBody[T](fn: PartialFunction[String, T]): Option[T] = {
- var resp: Option[T] = None
- def loop(): Option[T] = {
+ def readObjectBody(fn: ObjectParseFunc) {
+ def loop() {
next() match {
case EndObject => // done
case FieldName(name) =>
+ if (fn.isDefinedAt(name)) fn(name) else skipNext()
+ loop()
+ case token => unexpected(token, "field name")
+ }
+ }
+ loop()
+ }
+
+ /**
+ * alias for mapObject
+ */
+ def map[T](fn: PartialFunction[String, T]): Option[T] = mapObject(fn)
+
+ /**
+ * Reads the body of the object, up to the close-curly, returning the *last*
+ * invocation of the supplied partial function. Any field name not
+ * recognized by the given PartialFunction will be skipped.
+ * The result of calling this is undefined if not already in an object.
+ * If the PartialFunction matches a field name, it MUST either fully
+ * read the corresponding value or skip it. Not doing so will leave
+ * the parser in an unpredictable state.
+ */
+ def mapObject[T](fn: PartialFunction[String, T]): Option[T] = {
+ startObject()
+ mapObjectBody(fn)
+ }
+
+ /**
+ * Reads the body of the object, up to the close-curly, returning the *last*
+ * invocation of the supplied partial function. Any field name not
+ * recognized by the given PartialFunction will be skipped.
+ * The result of calling this is undefined if not already in an object.
+ * If the PartialFunction matches a field name, it MUST either fully
+ * read the corresponding value or skip it. Not doing so will leave
+ * the parser in an unpredictable state.
+ */
+ def mapObjectBody[T](fn: PartialFunction[String, T]): Option[T] = {
+ var res: Option[T] = None
+ def loop() {
+ next() match {
+ case EndObject => // done
+ case FieldName(name) => {
if (fn.isDefinedAt(name)) {
- resp = Some(fn(name))
+ res = Some(fn(name))
} else {
skipNext()
}
loop()
+ }
case token => unexpected(token, "field name")
}
- resp
}
loop()
+ res
}
/**
@@ -225,7 +267,7 @@ class Streamy(parser: JsonParser, val source: String) {
* The given function MUST either fully read the corresponding value
* or skip it. Not doing so will leave the parser in an unpredictable state.
*/
- def readArray[T](fn: Function[Int, T]) = {
+ def readArray(fn: ArrayParseFunc) {
startArray()
readArrayBody(fn)
}
@@ -237,7 +279,7 @@ class Streamy(parser: JsonParser, val source: String) {
* The given function MUST either fully read the corresponding value
* or skip it. Not doing so will leave the parser in an unpredictable state.
*/
- def readArrayOption[T](fn: Function[Int, T]): Boolean = {
+ def readArrayOption(fn: ArrayParseFunc): Boolean = {
startArrayOption() && { readArrayBody(fn); true }
}
@@ -246,7 +288,7 @@ class Streamy(parser: JsonParser, val source: String) {
* The given function MUST either fully read the corresponding value
* or skip it. Not doing so will leave the parser in an unpredictable state.
*/
- def readArrayBody[T](fn: Function[Int, T]) = {
+ def readArrayBody(fn: ArrayParseFunc) {
def loop(index: Int) {
if (peek() == EndArray) {
next() // skip ]
@@ -258,22 +300,6 @@ class Streamy(parser: JsonParser, val source: String) {
loop(0)
}
- def mapArray[T](fn: Function[Int, T]): Seq[T] = {
- startArray()
- mapArrayBody(fn)
- }
-
- def mapArrayBody[T](fn: Function[Int, T]): Seq[T] = {
- def loop(index: Int): List[T] = {
- if (peek() == EndArray) {
- next() // skip ]
- Nil
- } else {
- fn(index) :: loop(index + 1)
- }
- }
- loop(0)
- }
/**
* Reads an array using an accumulator.
* The given function MUST either fully read the corresponding value
@@ -302,6 +328,31 @@ class Streamy(parser: JsonParser, val source: String) {
}
/**
+ * Reads an array, returning a sequence that is the result of applying
+ * the given function to each element of the list
+ */
+ def mapArray[T](fn: Function[Int, T]): Seq[T] = {
+ startArray()
+ mapArrayBody(fn)
+ }
+
+ /**
+ * Reads an array, returning a sequence that is the result of applying
+ * the given function to each element of the list
+ */
+ def mapArrayBody[T](fn: Function[Int, T]): Seq[T] = {
+ def loop(index: Int): List[T] = {
+ if (peek() == EndArray) {
+ next() // skip ]
+ Nil
+ } else {
+ fn(index) :: loop(index + 1)
+ }
+ }
+ loop(0)
+ }
+
+ /**
* If the next value is "null", it is read and returned. Otherwise,
* nothing happens.
*/
View
6 src/test/scala/com/twitter/streamyj/StreamySpec.scala
@@ -249,15 +249,15 @@ object StreamySpec extends Specification {
arr mustEqual List(List(1L), 2L, 3L, 4L)
}
- "support returning values from function" in {
+ "support mapping over objects" in {
val s = Streamy("""{"bar":"testing"}""")
- val text = s.readObject{ case "bar" => s.readString() }
+ val text = s.mapObject{ case "bar" => s.readString() }
text mustEqual Some("testing")
}
"support mapping values from arrays" in {
val s = Streamy("""{"bar":[1,2,3,4,5]}""")
- val neg = s.readObject {
+ val neg = s.mapObject {
case "bar" => s.mapArray { case i => -1 * s.readInt() }
}
neg mustEqual Some(List(-1, -2, -3, -4, -5))
Please sign in to comment.
Something went wrong with that request. Please try again.