Skip to content

Instantly share code, notes, and snippets.

@Arneball
Created March 7, 2014 16:25
Show Gist options
  • Save Arneball/9414661 to your computer and use it in GitHub Desktop.
Save Arneball/9414661 to your computer and use it in GitHub Desktop.
Json Quasiquotes example
sealed trait JsValue {
override final def toString = mkString
def mkString: String
}
case class JsArray(value: JsValue*) extends JsValue {
def mkString = value.map{ _.mkString }.mkString("[", ", ", "]")
}
case class JsObj(value: (String, JsValue)*) extends JsValue {
def mkString = value.map{
case (k, v) => s""""$k": ${v.mkString}"""
}.mkString("{", ", ", "}")
}
case class JsString(value: String) extends JsValue{
def mkString = s""""$value""""
}
case class JsNumber(value: BigDecimal) extends JsValue {
def mkString = value.toString
}
trait Json[T]{
def toJson(t: T): JsValue
}
object Json {
def js[T](fun: T => JsValue) = new Json[T]{
def toJson(t: T) = fun(t)
}
implicit def any[T]: Json[T] = macro JsonHelper.apply_impl[T]
implicit val int = js[Int](JsNumber.apply(_))
implicit val str = js[String](JsString.apply(_))
implicit def seq[T : Json, CC[X]<:Iterable[X]]: Json[CC[T]] = new Json[CC[T]]{
def toJson(values: CC[T]) = {
val result = values.map{ value =>
implicitly[Json[T]].toJson(value)
}
JsArray(result.toSeq: _*)
}
}
}
object JsonHelper {
def apply_impl[T : c.WeakTypeTag](c: Context): c.Expr[Json[T]] = {
import c.universe._
val tTyp = weakTypeOf[T]
val fields = tTyp.declarations.collect {
case sym: MethodSymbol if sym.isCaseAccessor => (q"a.${sym.name}", sym.typeSignature, sym.name.encoded)
}
if (fields.isEmpty) {
c.abort(c.enclosingPosition, "Not a case class!")
} else {
val scalaSym = typeOf[Any].typeSymbol.owner
tTyp.baseClasses.collectFirst {
case sym if sym.name.decoded.startsWith("Tuple") && sym.owner == scalaSym =>
c.abort(c.enclosingPosition, "Not needed for tuples!")
}.getOrElse{
val selects = fields.map{ case (select, typeOfField, nameOfField) =>
q""" ($nameOfField -> implicitly[Json[$typeOfField]].toJson($select) ) """
}
c.Expr[Json[T]]{
q"""new Json[$tTyp]{
def toJson(a: $tTyp) = JsObj(..$selects)
}"""
}
}
}
}
}
====
import JsonHelper._
case class Horse(horsename: String, age: Int)
case class Rider(name: String, horse: Horse)
case class Stable(horses: IndexedSeq[Horse]) {
def this(h: Horse*) = this(h.toIndexedSeq)
}
object HorseTester extends App {
private def printJson[T: Json](t: T) = println{
implicitly[Json[T]].toJson(t)
}
printJson(new Horse("SpearHorse", 3))
printJson(Rider("Arne", Horse("Brunte", 22)))
printJson(new Stable(Horse("brunte", 3), Horse("curry", 5)))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment