|
|
/*
* Copyright (C) 2009-2011 Mathias Doenitz
* Inspired by a similar implementation by Nathan Hamblen
* (https://github.com/n8han/Databinder-Dispatch)
*
* 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 cc.spray.json
import formats._
import collection.mutable.ListBuffer
/**
* The general type of a JSON AST node.
*/
sealed trait JsValue {
override def toString = CompactPrinter(this)
def toString(printer: (JsValue => String)) = printer(this)
def fromJson[T :JsonReader]: T = jsonReader.read(this)
}
object JsValue {
/**
* General converter to a JsValue.
* Throws an IllegalArgumentException of the given value cannot be converted.
*/
def apply(value: Any): JsValue = value match {
case null => JsNull
case true => JsTrue
case false => JsFalse
case x: JsValue => x
case x: String => JsString(x)
case x: Int => JsNumber(x)
case x: Long => JsNumber(x)
case x: Double => JsNumber(x)
case x: Char => JsString(String.valueOf(x))
case x: Float => JsNumber(x)
case x: Byte => JsNumber(x)
case x: Short => JsNumber(x)
case x: BigInt => JsNumber(x)
case x: BigDecimal => JsNumber(x)
case x: Symbol => JsString(x.name)
case x: collection.Map[_, _] => JsObject(fromSeq(x))
case x@ collection.Seq((_, _), _*) => JsObject(fromSeq(x.asInstanceOf[Seq[(_, _)]]))
case x: collection.Seq[_] => JsArray(x.toList.map(JsValue.apply))
case x => throw new IllegalArgumentException(x.toString + " cannot be converted to a JsValue")
}
private def fromSeq(seq: Iterable[(_, _)]) = {
val list = ListBuffer.empty[JsField]
seq.foreach {
case (key: String, value) => list += JsField(key, JsValue(value))
case (key: Symbol, value) => list += JsField(key.name, JsValue(value))
case (key: JsString, value) => list += JsField(key.value, JsValue(value))
case (x, _) => throw new IllegalArgumentException(x.toString + " cannot be converted to a JsString")
}
list.toList
}
}
/**
* A JSON object.
*/
case class JsObject(fields: List[JsField]) extends JsValue {
lazy val asMap: Map[String, JsValue] = {
val b = Map.newBuilder[String, JsValue]
for (JsField(name, value) <- fields) b += ((name, value))
b.result()
}
}
object JsObject {
def apply(members: JsField*) = new JsObject(members.toList)
}
/**
* The members/fields of a JSON object.
*/
case class JsField(name: String, value: JsValue) extends JsValue
object JsField {
def apply(name: String, value: Any) = new JsField(name, JsValue(value))
}
/**
* A JSON array.
*/
case class JsArray(elements: List[JsValue]) extends JsValue
object JsArray {
def apply(elements: JsValue*) = new JsArray(elements.toList)
}
/**
* A JSON string.
*/
case class JsString(value: String) extends JsValue
/**
* A JSON number.
*/
case class JsNumber(value: BigDecimal) extends JsValue
object JsNumber {
def apply(n: Int) = new JsNumber(BigDecimal(n))
def apply(n: Long) = 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))
}
/**
* JSON Booleans.
*/
sealed trait JsBoolean extends JsValue {
def value: Boolean
}
object JsBoolean {
def apply(x: Boolean): JsBoolean = if (x) JsTrue else JsFalse
def unapply(x: JsBoolean): Option[Boolean] = Some(x.value)
}
case object JsTrue extends JsBoolean {
def value = true
}
case object JsFalse extends JsBoolean {
def value = false
}
/**
* The representation for JSON null.
*/
case object JsNull extends JsValue
|