summaryrefslogtreecommitdiff
path: root/src/main/scala/cc/spray/json/JsValue.scala
blob: 8781f73969a91fdd13b302d87e487be02a9fb00d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*
 * Original implementation (C) by the databinder-dispatch team
 * https://github.com/n8han/Databinder-Dispatch
 * Adapted and extended in 2011 by Mathias Doenitz
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

package cc.spray.json

import formats._
import collection.mutable.ListBuffer

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 {
  def apply(x: Any): JsValue = x match {
    case null => JsNull
    case true => JsTrue
    case false => JsFalse
    case x: JsValue => x
    case x: String => JsString(x)
    case x: Symbol => JsString(x.name)
    case x: Int => JsNumber(x)
    case x: Long => JsNumber(x)
    case x: Short => JsNumber(x)
    case x: Byte => JsNumber(x)
    case x: Float => JsNumber(x)
    case x: Double => JsNumber(x)
    case x: BigInt => JsNumber(x)
    case x: BigDecimal => JsNumber(x)
    case x: Char => JsString(String.valueOf(x))
    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
  }

  def fromString(json: String) = JsonParser(json)
  def toString(value: JsValue, printer: (JsValue => String) = CompactPrinter) = printer(value)
}

case class JsString(value: String) extends JsValue


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))
}

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)
}


case class JsField(name: String, value: JsValue) extends JsValue

object JsField {
  def apply(name: String, value: Any) = new JsField(name, JsValue(value))
}


case class JsArray(elements: List[JsValue]) extends JsValue

object JsArray {
  def apply(elements: JsValue*) = new JsArray(elements.toList)
}


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
}


case object JsNull extends JsValue