Skip to content

Instantly share code, notes, and snippets.

@jcracknell
Created December 18, 2013 23:11
Show Gist options
  • Save jcracknell/8031491 to your computer and use it in GitHub Desktop.
Save jcracknell/8031491 to your computer and use it in GitHub Desktop.
Abstracting away your JSON DOM with a 'dialect' mixin.
trait JsonDialect { dialect =>
import scala.language.implicitConversions
type JValue
type JArray <: JValue
type JBoolean <: JValue
type JNull <: JValue
type JNumber <: JValue
type JObject <: JValue
type JString <: JValue
/** Factory and utility methods for JSON values.
* @group Factories */
def JValue: JValueCompanion
trait JValueCompanion {
/** Decodes a JSON value from the provided string. */
def decode(json: String): JValue
/** Decodes a JSON value from the provide [[java.io.Reader]]. */
def decode(reader: java.io.Reader): JValue
/** Encodes the provided JSON value to a string. */
def encode(value: JValue): String
/** Encodes the provided JSON value to the provided [[java.io.Writer]]. */
def encode(value: JValue, writer: java.io.Writer): Unit
def mkArray(elements: Seq[JValue]): JArray
def mkBoolean(value: Boolean): JBoolean
def mkNumber(value: Double): JNumber
def mkObject(properties: Seq[(String, JValue)]): JObject
def mkString(value: String): JString
def asArray(value: JValue): Option[JArray]
def asBoolean(value: JValue): Option[JBoolean]
def asNull(value: JValue): Option[JNull]
def asNumber(value: JValue): Option[JNumber]
def asObject(value: JValue): Option[JObject]
def asString(value: JValue): Option[JString]
def unpackArray(a: JArray): Seq[JValue]
def unpackBoolean(b: JBoolean): Boolean
def unpackNumber(n: JNumber): Double
def unpackObject(o: JObject): Seq[(String, JValue)]
def unpackString(s: JString): String
}
trait JValueConversion[+A <: JValue] {
def apply(value: JValue): Option[A]
}
object JValueConversion {
def apply[A <: JValue](conv: JValue => Option[A]): JValueConversion[A] =
new JValueConversion[A] { def apply(value: JValue): Option[A] = conv(value) }
implicit val valueConversion: JValueConversion[JValue] = apply(x => Some(x))
implicit val arrayConversion: JValueConversion[JArray] = apply(JValue.asArray)
implicit val booleanConversion: JValueConversion[JBoolean] = apply(JValue.asBoolean)
implicit val nullConversion: JValueConversion[JNull] = apply(JValue.asNull)
implicit val numberConversion: JValueConversion[JNumber] = apply(JValue.asNumber)
implicit val objectConversion: JValueConversion[JObject] = apply(JValue.asObject)
implicit val stringConversion: JValueConversion[JString] = apply(JValue.asString)
}
object JArray {
def apply(): JArray = JValue.mkArray(Nil)
def apply(elements: Seq[JValue]): JArray = JValue.mkArray(elements)
def apply(e0: JValue, es: JValue*): JArray = apply(e0 +: es)
def unapplySeq(value: JValue): Option[Seq[JValue]] = JValue.asArray(value).map(JValue.unpackArray)
}
object JBoolean {
def apply(value: Boolean): JBoolean = JValue.mkBoolean(value)
def unapply(value: JValue): Option[Boolean] = JValue.asBoolean(value).map(JValue.unpackBoolean)
}
val JNull: JNull
object JNumber {
def apply(value: Double): JNumber = JValue.mkNumber(value)
def unapply(value: JValue): Option[Double] = JValue.asNumber(value).map(JValue.unpackNumber)
}
object JObject {
def apply(): JObject = JValue.mkObject(Nil)
def apply(properties: Seq[(String, JValue)]): JObject = JValue.mkObject(properties)
def apply(p0: (String, JValue), ps: (String, JValue)*): JObject = apply(p0 +: ps)
def unapply(value: JValue): Option[Seq[(String, JValue)]] = JValue.asObject(value).map(JValue.unpackObject)
}
object JString {
def apply(value: String): JString = JValue.mkString(value)
def unapply(value: JValue): Option[String] = JValue.asString(value).map(JValue.unpackString)
}
implicit class JValueOps(value: JValue) {
/** Encodes the JSON value to a string. */
def encoded: String = JValue.encode(value)
/** Encodes the JSON value to the provided [[java.io.Writer]]. */
def encodeTo(writer: java.io.Writer): Unit = JValue.encode(value, writer)
/** Attempt to case the JSON value to the provided dialect type. This method exists in order to enable
* conversions using type parameters. */
def as[A <: JValue](implicit conv: JValueConversion[A]): Option[A] = conv(value)
/** Attempt to cast the JSON value to the dialect's array type. */
def asArray: Option[JArray] = JValue.asArray(value)
/** Attempt to cast the JSON value to the dialect's boolean type. */
def asBoolean: Option[JBoolean] = JValue.asBoolean(value)
/** Attempt to cast the JSON value to the dialect's null type. */
def asNull: Option[JNull] = JValue.asNull(value)
/** Attempt to cast the JSON value to the dialect's number type. */
def asNumber: Option[JNumber] = JValue.asNumber(value)
/** Attempt to cast the JSON value to the dialect's object type. */
def asObject: Option[JObject] = JValue.asObject(value)
/** Attempt to cast the JSON value to the dialect's string type. */
def asString: Option[JString] = JValue.asString(value)
}
/** Operations defined on the dialect's array type. */
implicit class JArrayOps(a: JArray) {
/** The JSON array value. */
def value = JValue.unpackArray(a)
}
/** Operations defined on the dialect's boolean type. */
implicit class JBooleanOps(b: JBoolean) {
/** The JSON boolean value. */
def value = JValue.unpackBoolean(b)
}
/** Operations defined on the dialect's number type. */
implicit class JNumberOps(n: JNumber) {
/** The JSON number value. */
def value = JValue.unpackNumber(n)
}
/** Operations defined on the dialect's object type. */
implicit class JObjectOps(o: JObject) {
/** The JSON object value. */
def value = JValue.unpackObject(o)
}
/** Operations defined on the dialect's string type. */
implicit class JStringOps(s: JString) {
/** The JSON string value. */
def value = JValue.unpackString(s)
}
}
import com.eclipsesource.json._
import scala.collection.JavaConversions._
trait MinimalJsonDialect extends JsonDialect {
type JValue = com.eclipsesource.json.JsonValue
type JArray = com.eclipsesource.json.JsonArray
type JBoolean = com.eclipsesource.json.JsonValue
type JNull = com.eclipsesource.json.JsonValue
type JNumber = com.eclipsesource.json.JsonValue
type JObject = com.eclipsesource.json.JsonObject
type JString = com.eclipsesource.json.JsonValue
val JNull: JNull = JsonValue.NULL
object JValue extends JValueCompanion {
def decode(str: String): JValue = JsonValue.readFrom(str)
def decode(reader: java.io.Reader): JValue = JsonValue.readFrom(reader)
def encode(value: JValue): String = value.toString
def encode(value: JValue, writer: java.io.Writer): Unit = value.writeTo(writer)
def mkArray(elements: Seq[JValue]): JArray = (new JsonArray /: elements) { (arr, e) => arr.add(e) }
def mkBoolean(value: Boolean): JBoolean = if(value) JsonValue.TRUE else JsonValue.FALSE
def mkNumber(value: Double): JNumber = JsonValue.valueOf(value)
def mkObject(properties: Seq[(String, JValue)]): JObject = (new JsonObject /: properties) { (obj, p) => obj.add(p._1, p._2) }
def mkString(value: String): JString = JsonValue.valueOf(value)
def asArray(value: JValue): Option[JArray] = if(value.isArray) Some(value.asArray()) else None
def asBoolean(value: JValue): Option[JBoolean] = if(value.isBoolean) Some(value.asInstanceOf[JBoolean]) else None
def asNull(value: JValue): Option[JNull] = if(value.isNull) Some(value.asInstanceOf[JNull]) else None
def asNumber(value: JValue): Option[JNumber] = if(value.isNumber) Some(value.asInstanceOf[JNumber]) else None
def asObject(value: JValue): Option[JObject] = if(value.isObject) Some(value.asInstanceOf[JObject]) else None
def asString(value: JValue): Option[JString] = if(value.isString) Some(value.asInstanceOf[JString]) else None
def unpackArray(a: JArray): Seq[JValue] = a.toSeq
def unpackBoolean(b: JBoolean): Boolean = b.isTrue()
def unpackNumber(n: JNumber): Double = n.asDouble()
def unpackObject(o: JObject): Seq[(String, JValue)] = o.view.map(m => (m.getName, m.getValue)).toSeq
def unpackString(s: JString): String = s.asString()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment