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

Initial json module. Add production files and pom.

  • Loading branch information...
Joni Freeman
Joni Freeman committed Aug 11, 2009
1 parent 215ca9f commit eca4bf99b807b05de600fe1ad454153c0b6477a5
View
@@ -0,0 +1,31 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>lift</artifactId>
<groupId>net.liftweb</groupId>
<version>1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>lift-json</artifactId>
<!-- <packaging>jar</packaging> -->
<name>Lift Json</name>
<description>Json utilities</description>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
</dependency>
<dependency>
<groupId>org.scala-tools.testing</groupId>
<artifactId>specs</artifactId>
<version>1.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,91 @@
package net.liftweb.json
import scala.reflect.Manifest
import JsonAST._
/** Function to extract values from JSON AST using case classes.
* Note, this is experimental and not fully implemented yet.
*
* See: example.scala "Extraction example", "Partial extraction example"
*/
object Extraction {
/** Intermediate format which describes the mapping.
* This ADT is constructed (and then memoized) from given case class using reflection.
*
* Example mapping.
*
* package xx
* case class Person(name: String, address: Address, children: List[Child])
* case class Address(street: String, city: String)
* case class Child(name: String, age: BigInt)
*
* will produce following Mapping:
*
* Constructor(None, "xx.Person", List(
* Value("name"),
* Constructor(Some("address"), "xx.Address", List(Value("street"), Value("city"))),
* ListConstructor("children", "xx.Child", List(Value("name"), Value("age")))))
*/
sealed abstract class Mapping
case class Value(path: String) extends Mapping
case class Constructor(path: Option[String], classname: String, args: List[Mapping]) extends Mapping
case class ListConstructor(path: String, classname: String, args: List[Mapping]) extends Mapping
// FIXME; should the return type be Either[MappingError, A] ?
def extract[A](json: JValue)(implicit mf: Manifest[A]) = {
val mapping = memoize(mf.erasure)
def newInstance(classname: String, args: List[Any]) = {
val clazz = Class.forName(classname)
val argTypes = args.map {
case x: List[_] => classOf[List[_]]
case x => x.asInstanceOf[AnyRef].getClass
}
clazz.getConstructor(argTypes.toArray: _*).newInstance(args.map(_.asInstanceOf[AnyRef]).toArray: _*)
}
def build(root: JValue, mapping: Mapping, argStack: List[Any]): List[Any] = mapping match {
case Value(path) => fieldValue(root, path).values :: argStack
case Constructor(path, classname, args) =>
val newRoot = path match {
case Some(p) => root \ p
case None => root
}
newInstance(classname, args.flatMap(build(newRoot, _, argStack))) :: Nil
case ListConstructor(path, classname, args) =>
val arr = fieldValue(root, path).asInstanceOf[JArray]
arr.arr.map(elem => newInstance(classname, args.flatMap(build(elem, _, argStack)))) :: argStack
}
def fieldValue(json: JValue, path: String) = (json \ path).asInstanceOf[JField].value
build(json, mapping, Nil).head
}
// FIXME memoize
private def memoize(clazz: Class[_]) = {
def makeMapping(path: Option[String], clazz: Class[_], isList: Boolean): Mapping = isList match {
case false => Constructor(path, clazz.getName, constructorArgs(clazz))
case true => ListConstructor(path.get, clazz.getName, constructorArgs(clazz))
}
// FIXME add rest of the primitives
def constructorArgs(clazz: Class[_]) = clazz.getDeclaredFields.map { x =>
if (x.getType == classOf[String]) Value(x.getName)
else if (x.getType == classOf[BigInt]) Value(x.getName)
else if (x.getType == classOf[List[_]]) makeMapping(Some(x.getName), Util.parametrizedType(x), true)
else makeMapping(Some(x.getName), x.getType, false)
}.toList.reverse // FIXME Java6 returns these in reverse order, verify that and check other vms
makeMapping(None, clazz, false)
}
}
object Util {
import java.lang.reflect._
def parametrizedType(f: Field): Class[_] = {
val ptype = f.getGenericType.asInstanceOf[ParameterizedType]
ptype.getActualTypeArguments()(0).asInstanceOf[Class[_]]
}
}
@@ -0,0 +1,215 @@
package net.liftweb.json
object JsonAST {
import scala.text.Document
import scala.text.Document._
sealed abstract class JValue {
type Values
def \(nameToFind: String): JValue = {
def find(xs: List[JValue]): List[JValue] = xs.flatMap {
case JObject(l) => l.filter {
case JField(name, value) if name == nameToFind => true
case _ => false
}
case JArray(l) => find(l)
case field @ JField(name, value) if name == nameToFind => field :: Nil
case _ => Nil
}
find(children) match {
case Nil => JNothing
case x :: Nil => x
case x => JArray(x)
}
}
// FIXME this must be tail recursive
def \\(nameToFind: String): JObject = {
def find(json: JValue): List[JField] = json match {
case JObject(l) => l.foldLeft(List[JField]())((a, e) => a ::: find(e))
case JArray(l) => l.foldLeft(List[JField]())((a, e) => a ::: find(e))
case field @ JField(name, value) if name == nameToFind => field :: find(value)
case JField(_, value) => find(value)
case _ => Nil
}
JObject(find(this))
}
def apply(i: Int): JValue = JNothing
def values: Values
def children = this match {
case JObject(l) => l
case JArray(l) => l
case JField(n, v) => List(v)
case _ => Nil
}
def find(p: JValue => Boolean): Option[JValue] = {
def find(json: JValue): Option[JValue] = {
if (p(json)) return Some(json)
json match {
case JObject(l) => l.flatMap(find _).firstOption
case JArray(l) => l.flatMap(find _).firstOption
case JField(_, value) => find(value)
case _ => None
}
}
find(this)
}
def filter(p: JValue => Boolean): List[JValue] = {
def filter(json: JValue, acc: List[JValue]): List[JValue] = {
val newAcc = if (p(json)) json :: acc else acc
json match {
case JObject(l) => l.foldLeft(newAcc)((a, e) => filter(e, a))
case JArray(l) => l.foldLeft(newAcc)((a, e) => filter(e, a))
case JField(_, value) => filter(value, newAcc)
case _ => newAcc
}
}
filter(this, Nil).reverse
}
def extract[A](implicit mf: scala.reflect.Manifest[A]) = Extraction.extract(this)(mf)
}
case object JNothing extends JValue {
type Values = Nothing
def values = error("nothing contains no values")
}
case object JNull extends JValue {
type Values = Null
def values = null
}
case class JString(s: String) extends JValue {
type Values = String
def values = s
}
case class JDouble(num: Double) extends JValue {
type Values = Double
def values = num
}
case class JInt(num: BigInt) extends JValue {
type Values = BigInt
def values = num
}
case class JBool(value: Boolean) extends JValue {
type Values = Boolean
def values = value
}
case class JField(name: String, value: JValue) extends JValue {
type Values = (String, value.Values)
def values = (name, value.values)
override def apply(i: Int): JValue = value(i)
}
case class JObject(obj: List[JField]) extends JValue {
type Values = Map[String, Any]
def values = Map() ++ obj.map(_.values.asInstanceOf[(String, Any)]) // FIXME compiler fails if cast is removed
}
case class JArray(arr: List[JValue]) extends JValue {
type Values = List[Any]
def values = arr.map(_.values)
override def apply(i: Int): JValue = arr(i)
}
def render(value: JValue): Document = value match {
case JBool(true) => text("true")
case JBool(false) => text("false")
case JDouble(n) => text(n.toString)
case JInt(n) => text(n.toString)
case JNull => text("null")
case JNothing => error("can't render 'nothing'")
case JString(s) => text("\"" + quote(s) + "\"")
case JArray(arr) => text("[") :: series(trimArr(arr).map(render(_))) :: text("]")
case JField(n, v) => text("\"" + n + "\":") :: render(v)
case JObject(obj) =>
val nested = break :: fields(trimObj(obj).map(f => text("\"" + f.name + "\":") :: render(f.value)))
text("{") :: nest(2, nested) :: break :: text("}")
}
private def trimArr(xs: List[JValue]) = xs.filter(_ != JNothing)
private def trimObj(xs: List[JField]) = xs.filter(_.value != JNothing)
private def fold(docs: List[Document]) = docs.foldLeft[Document](empty)(_ :: _)
private def series(docs: List[Document]) = fold(punctuate(text(","), docs))
private def fields(docs: List[Document]) = fold(punctuate(text(",") :: break, docs))
private def punctuate(p: Document, docs: List[Document]): List[Document] = docs match {
case Nil => Nil
case List(d) => List(d)
case d :: ds => (d :: p) :: punctuate(p, ds)
}
private def quote(s: String) = (s.map {
case '\r' => "\\r"
case '\n' => "\\n"
case '\t' => "\\t"
case '"' => "\\\""
case '\\' => "\\\\"
case c if ((c >= '\u0000' && c < '\u001f') || (c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) => "\\u%04x".format(c.asInstanceOf[Int])
case c => c
}).mkString
}
object JsonDSL extends Printer {
import JsonAST._
implicit def int2jvalue(x: Int) = JInt(x)
implicit def long2jvalue(x: Long) = JInt(x)
implicit def bigint2jvalue(x: BigInt) = JInt(x)
implicit def double2jvalue(x: Double) = JDouble(x)
implicit def bigdecimal2jvalue(x: BigDecimal) = JDouble(x.doubleValue)
implicit def boolean2jvalue(x: Boolean) = JBool(x)
implicit def string2jvalue(x: String) = JString(x)
implicit def seq2jvalue[A <% JValue](s: Seq[A]) = JArray(s.toList.map { a => val v: JValue = a; v })
implicit def option2jvalue[A <% JValue](opt: Option[A]): JValue = opt match {
case Some(x) => x
case None => JNothing
}
implicit def pair2jvalue[A <% JValue](t: (String, A)) = JObject(List(JField(t._1, t._2)))
implicit def list2jvalue(l: List[JField]) = JObject(l)
implicit def jobject2assoc(o: JObject) = new JsonListAssoc(o.obj)
implicit def pair2Assoc[A <% JValue](t: (String, A)) = new JsonAssoc(t)
class JsonAssoc[A <% JValue](left: (String, A)) {
def ~[B <% JValue](right: (String, B)) = {
val l: JValue = left._2
val r: JValue = right._2
JObject(JField(left._1, l) :: JField(right._1, r) :: Nil)
}
def ~(right: JObject) = {
val l: JValue = left._2
JObject(JField(left._1, l) :: right.obj)
}
}
class JsonListAssoc(left: List[JField]) {
def ~(right: (String, JValue)) = JObject(left ::: List(JField(right._1, right._2)))
def ~(right: JObject) = JObject(left ::: right.obj)
}
}
trait Printer {
import scala.text._
def compact(d: Document) = {
def layout(doc: Document): String = doc match {
case DocText(s) => s
case DocCons(d1, d2) => layout(d1) + layout(d2)
case DocBreak => ""
case DocNest(_, d) => layout(d)
case DocGroup(d) => layout(d)
case DocNil => ""
}
layout(d)
}
def pretty(d: Document) = {
val s = new java.io.StringWriter
d.format(80, s)
s.toString
}
}
Oops, something went wrong.

0 comments on commit eca4bf9

Please sign in to comment.