This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

incorporate jorge's json parser and reorg packages to be briefer

  • Loading branch information...
n8han committed Mar 9, 2009
1 parent 459eb17 commit 41edb939baa5c6edb4378c1bd8e1d2f10f3350f2
@@ -1,10 +1,10 @@
package net.databinder.dispatch.couch
package dispatch.json.couch
import java.io.InputStream
import java.net.URLEncoder.encode
import org.apache.http.HttpHost
import net.databinder.dispatch._
import json._
trait Doc extends Js {
val _id = Symbol("_id") ? str
@@ -1,4 +1,4 @@
package net.databinder.dispatch
package dispatch
import java.io.{InputStream,OutputStream,BufferedInputStream,BufferedOutputStream}
@@ -130,7 +130,7 @@ class Http(
val src = scala.io.Source.fromString(in)
thunk(scala.xml.parsing.XhtmlParser(src))
}
def $ [T](thunk: Js#M => T): T = >> { stm => thunk(Js(stm)) }
def $ [T](thunk: json.Js#M => T): T = >> { stm => thunk(json.Js(stm)) }
/** Ignore response body if OK. */
def >| = ok ((r,e) => ())
@@ -1,4 +1,4 @@
package net.databinder.dispatch
package dispatch.json
import scala.util.parsing.json.Parser
import scala.util.parsing.input.{Reader,StreamReader}
@@ -0,0 +1,128 @@
package dispatch.json
// http://paste.pocoo.org/raw/106688/
import scala.util.parsing.combinator._
import scala.util.parsing.combinator.syntactical._
import scala.util.parsing.combinator.lexical._
object JsonParser extends StdTokenParsers with ImplicitConversions {
type Tokens = scala.util.parsing.json.Lexer
val lexical = new Tokens
lexical.reserved ++= List("true", "false", "null")
lexical.delimiters ++= List("{", "}", "[", "]", ":", ",")
def jsonObj = "{" ~> repsep(objPair, ",") <~ "}" ^^ (JsObject.apply _)
def jsonArr = "[" ~> repsep(jsonVal, ",") <~ "]" ^^ (JsArray.apply _)
def objPair = jsonStr ~ (":" ~> jsonVal) ^^ { case x ~ y => (x, y) }
def jsonVal: Parser[JsValue] =
(jsonObj | jsonArr | jsonStr | jsonNum | "true" ^^^ JsTrue | "false" ^^^ JsFalse | "null" ^^^ JsNull)
def jsonStr = accept("string", { case lexical.StringLit(n) => JsString(n)})
def jsonNum = accept("number", { case lexical.NumericLit(n) => JsNumber(n) })
def apply(input: String): JsValue =
phrase(jsonVal)(new lexical.Scanner(input)) match {
case Success(result, _) => result
case _ => throw new Exception("Illegal JSON format")
}
}
sealed trait JsValue {
type T
def self: T
override def toString = JsValue.toJson(this)
}
case class JsString(override val self: String) extends JsValue {
type T = String
}
/**
* This can also be implemented with as a Double, even though BigDecimal is
* more loyal to the json spec.
* NOTE: Subtle bugs can arise, i.e.
* BigDecimal(3.14) != BigDecimal("3.14")
* such are the perils of floating point arithmetic.
*/
case class JsNumber(override val self: BigDecimal) extends JsValue {
type T = BigDecimal
}
// This can extend scala.collection.MapProxy to implement Map interface
case class JsObject(override val self: Map[JsString, JsValue]) extends JsValue {
type T = Map[JsString, JsValue]
}
// This can extend scala.SeqProxy to implement Seq interface
case class JsArray(override val self: List[JsValue]) extends JsValue {
type T = List[JsValue]
}
sealed abstract case class JsBoolean(b: Boolean) extends JsValue {
type T = Boolean
val self = b
}
case object JsTrue extends JsBoolean(true)
case object JsFalse extends JsBoolean(false)
case object JsNull extends JsValue {
type T = Null
val self = null
}
object JsObject {
def apply() = new JsObject(Map())
def apply(xs: Seq[(JsString, JsValue)]) = new JsObject(Map() ++ xs)
}
object JsNumber {
def apply(n: Int) = new JsNumber(BigDecimal(n))
def apply(n: Long) = new JsNumber(BigDecimal(n))
def apply(n: Float) = new JsNumber(BigDecimal(n))
def apply(n: Double) = new JsNumber(BigDecimal(n))
def apply(n: BigInt) = new JsNumber(BigDecimal(n))
def apply(n: String) = new JsNumber(BigDecimal(n))
}
object JsString {
def apply(x: Any): JsString = x match {
case s: Symbol => new JsString(s.name)
case s: String => new JsString(s)
// This is a hack needed for JsObject.
// The other option is to throw an exception here.
case _ => new JsString(x.toString)
}
}
object JsValue {
def apply(x: Any): JsValue = x match {
case null => JsNull
case j: JsValue => j
case true => JsTrue
case false => JsFalse
case s @ (_: String | _: Symbol) => JsString(s)
case n: Int => JsNumber(n)
case n: Long => JsNumber(n)
case n: Float => JsNumber(n)
case n: Double => JsNumber(n)
case n: BigInt => JsNumber(n)
case n: BigDecimal => JsNumber(n)
case xs: List[_] => JsArray(xs.map(JsValue.apply))
case m: scala.collection.Map[_, _] => JsObject(
Map.empty ++ (for ((key, value) <- m) yield (JsString(key), JsValue(value)))
)
case xs: Seq[_] => JsArray(xs.map(JsValue.apply).toList)
}
def fromString(s: String) = JsonParser(s)
def toJson(x: JsValue): String = x match {
case JsNull => "null"
case JsBoolean(b) => b.toString
case JsString(s) => "\"" + s + "\""
case JsNumber(n) => n.toString
case JsArray(xs) => xs.map(toJson).mkString("[",", ","]")
case JsObject(m) => m.map{case (key, value) => toJson(key) + " : " + toJson(value)}.mkString("{",", ","}")
}
}
@@ -1,4 +1,6 @@
package net.databinder.dispatch.times
package dispatch.json.times
import json._
trait Times extends Js {
lazy val http = new Http("api.nytimes.com")
@@ -1,22 +1,27 @@
import org.scalatest.Spec
class JsonSpec extends Spec {
import net.databinder.dispatch._
import dispatch.json._
import Js._
val js = Js(""" { "a": {"a": "a string", "b": {"pi": 3.14159265 } }, "b": [1,2,3] } """)
val js = JsValue.fromString(""" { "a": {"a": "a string", "b": {"pi": 3.14159265 } }, "b": [1,2,3] } """)
JsValue.fromString(JsValue.toJson(js)) == js
describe("Parsed Json") {
it("should equal expected map") {
assert( js === Map(
'a -> Some(Map(
'a -> Some("a string"),
'b -> Some(Map(
'pi -> Some(3.14159265)
))
)),
'b -> Some(List(Some(1), Some(2), Some(3)))
))
assert( js === JsValue(Map(
'a -> Map(
'a -> "a string",
'b -> Map(
'pi -> BigDecimal("3.14159265")
)
),
'b -> List(1, 2, 3)
)))
}
it("should equal itself serilized and reparsed") {
assert(js === JsValue.fromString(JsValue.toJson(js)))
}
}
describe("Layered extractor object") {

0 comments on commit 41edb93

Please sign in to comment.