开发者

How can I add extra behaviour only to Lists of a certain type?

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 开发者_如何学JAVAdifferent 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)
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜