I have a small Scala/Neo4j application that links people and topics through "skilledAt" and "interestedIn" relations. It has a REST/Json Api (using Scalatra) and I ran into a typical type-erasure problem when I wanted to add an "asJson" method to List[Person] and List[Topic]. I would like to implement different Json serialization behaviour for the different content types but of course the types get erased. The best I've been able to come up with so far is the following runtime trick:
implicit def topicsOrPeopleAsJson[T](list: List[T]) = new {
def asJson: String = {
list match {
case head :: tail if (head.isInstanceOf[Topic]) => topicsToJson(list.asInstanceOf[List[Topic]])
case head :: tail if (head.isInstanceOf[Person]) => peopleToJson(list.asInstanceOf[List[Person]])
case _ => "[]"
}
}
private def peopleToJson(people: List[Person]) = {
...
}
private def topicsToJson(topics: List[Topic]) = {
...
}
}
This works just fine but I was wondering whether there was a better solution, maybe something including type classes, a topic I'm not very familiar with (yet).
Use another level of implicit (this is typeclasses indeed):
trait ListToJsonConverter[T] {
def asJson(l: List[T]) : String
}
implicit object PeopleToJsonConverter extends ListToJsonConverter[Person] {...}
implicit object TopicToJsonConverter extends ListToJsonConverter[Topic] {...}
implicit object DefaultJsonConverter extends ListToJsonConverter[Any] {
def asJson(l: List[Any]) = "[]"
}
implicit def topicsOrPeopleAsJson[T](list: List[T])(implicit ev : ListToJsonConverter[T]) = new {
def asJson = ev.asJson(list)
}
This may not be exactly what you asked for however. The converter will be chosen at compile time. So if you call with a list of person which the compiler knows only as a List[Any], it will not work as expected.
Why not do it the OO way?
trait JSONable {
def toJSON:String
}
class Person
class Topics
implicit def persontoJSONable(p:Person) = new PersonSerializer(p)
implicit def topicToJSONable(t:Topic) = new PersonSerializer(t)
class PersonSerializer(p:Person) extends JSONable {
override def toJSON = {
//...
}
}
class TopicSerializer(t:Topic) extends JSONable {
override def toJSON = {
//...
}
}
def ListAsJSON[T <% JSONable](l:List[T]) = {
l.map(_.toJSON)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With