Reputation: 7110
The Play Framework provides a method of converting objects to JSON via an implicit Writes
.
def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue
I have a partial function that is processing messages, some of which may be serializable to JSON if a Writes exists. How can I write a case that matches objects for which an implicit Writes exists?
override def receive = {
case obj:/*(T where there exists an implicit Writes[T])*/ =>
return Json.toJson(obj)
case other =>
return Json.obj("unknown" -> other.toString)
}
Essentially what I want is a shorter version of
case obj:ClassA =>
return Json.toJson(obj)
case obj:ClassB =>
return Json.toJson(obj)
case obj:ClassC =>
return Json.toJson(obj)
// ... repeat 20 times for all the classes where I know I have a Writes
I tried making like an unapply
that accepts an implicit but I can't get case to accept that without a syntax error.
Upvotes: 2
Views: 1324
Reputation: 4804
You have two options:
Writes[ClassA]
, Writes[ClassB]
, etc in your functionYou might have to define another partial function and have receive
call that since you don't have the signature
Writes
instance directly to convert to Json// companion object of ClassA
object ClassA {
val jsonWrites: Writes[ClassA]
}
And since inside the case matching scope you know it's a ClassA, you can use the Writes[ClassA] instance directly and everything will typecheck.
case obj: ClassA =>
return ClassA.jsonWrites.writes(obj)
EDIT:
A third option that is both less error prone and flexible is to define a list of functions that 1. check whether an obj: Any
is an instance of T
and if so converts it to Json.
Here's a minimal snippet I have working using Scala worksheet. See checkIsInstanceAndConvertToJson
:
import scala.reflect.ClassTag
case class Json(str: String)
trait Writes[A] {
def writes(obj: A): Json
}
// Generate a function that
// 1. Checks whether an object (Any) is an instance of `T`
// 2. Convert it to Json if it is
// The implementation of checking function. All unsafety is encapsulated
def checkIsInstanceAndConvertToJson[T: Writes](implicit t: ClassTag[T]): Any => Option[Json] = (obj: Any) => {
if (t.runtimeClass.isInstance(obj)) {
Some(implicitly[Writes[T]].writes(obj.asInstanceOf[T]))
}
else None
}
// ==========
// USAGE
// ==========
object Foo {
implicit val jsonWrites = new Writes[Foo] {
override def writes(obj: Foo) = Json("Foo")
}
}
class Foo
object Bar {
implicit val jsonWrites: Writes[Bar] = new Writes[Bar] {
override def writes(obj: Bar) = Json("Bar")
}
}
class Bar
// Defining a list of functions that checks whether
// an object is of an instance, and if so, converts it to Json
val checkFuncs = Vector[Any => Option[Json]](
checkIsInstanceAndConvertToJson[Bar],
checkIsInstanceAndConvertToJson[Foo]
)
val t: Any = new Bar
val iter = checkFuncs.iterator
var json: Option[Json] = None
while (json.isEmpty && iter.hasNext) {
val func = iter.next()
json = func(t)
}
println(json)
The above snippets prints Some(Json(Bar))
Upvotes: 2