user7212552
user7212552

Reputation:

Generate case classes serializer and deserializer implicitly using play-json

I'm using play-json to map Json to case classes or enums. I'm looking for a smart way of creating Formats implicitly, since my project contains many types definitions.


At the moment I created a simple function to generate Formats for Enums:

def formatEnum[E <: Enumeration](enum: E) = Format(Reads.enumNameReads(enum), Writes.enumNameWrites)

But it takes a non-implicit argument so it cannot be used as implicit converter.


I tried to do the same for case classes:

implicit def caseFormat[A] = Json.format[A]

But I get the error "No unapply or unapplySeq function found", since Json.format is a macro which inspect the structure of the class.

Then I tried to create my macro in this way:

import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context

implicit def caseFormat[A](): Format[A] = macro impl[A]

def impl[A: c.WeakTypeTag](c: Context)(): c.Expr[Reads[A]] = {
    import c.universe._
    val TypeRef(pre, sym, args) = weakTypeTag[A].tpe
    val t = args.head
    val expr =  q"Json.format[$t]"
    c.Expr[Reads[A]](expr)
}

But the compiler does not find the implicit Format, though there is an implicit def that should generate the value.


Of course I can simply define many implicit val, but I think there is a smarter way to do it.

Upvotes: 0

Views: 617

Answers (1)

phantomastray
phantomastray

Reputation: 449

Assuming you have lots of case classes and you wish to json serialize it on the fly without having to write a play-json writer.

import play.api.libs.json._
import scala.reflect.runtime.{universe => ru}
implicit class writeutil[T: ru.TypeTag](i: T) {
    implicit val writer = Json.writes[T]

    def toJson() = Json.toJson(i)
}

def toInstance[T: ru.TypeTag](s: String): Option[T] = {
    implicit val reader = Json.reads[T]
    Json.fromJson[T](Json.parse(s)) match {
        case JsSuccess(r: T, path: JsPath) => Option(r)
        case e: JsError => None
    }
}

An optimal implementation would be to re-use the reader/writer by caching and lookup. You can also read more about play-json.

You can use this as:

case class Entity(a: String, b: Int)
val e = Entity("Stack", 0)

e.toJson()

Upvotes: 0

Related Questions