scalaliftscala-2.10lift-json

Designing serialization library in Scala with type classes


I have system where I need to serialize different kinds of objects to json and xml. Some of them are Lift MetaRecords, some are case classes. I wanted to use type classes and create something like:

trait Serializable[T] {
  serialize[T](obj: T): T
}

And usual implementations for json, xml and open for extension.

Problem I'm facing now is serialization itself. Currently there are different contexts in which objects are serialized. Imagine news feed system. There are three objects: User, Post (feed element) and Photo. Those objects have some properties and can reference each other. Now in same cases I want to serialize object alone (user settings, preferences, etc.) in other cases I need other objects to be serialized as well ie. Feed: List[Post] + related photos. In order to do that I need to provide referenced objects.

My current implementation is bloated with optional parametered functions.

def feedAsJson(post: MPost, grp: Option[PrivateGroup], commentsBox: Option[List[MPostComment]] = Empty): JObject

I thought about implementing some kind of context solution. Overload feedAsJson with implicit context parameter that will provide necessary data. I don't know how I'd like to implement it yet as it touches database maybe with cake pattern. Any suggestions very appreciated.


Solution

  • Can't you put the implicits in scope that will create the right kind of serializers that you need? Something to that effect:

    def doNothingSerializer[T]: Serializable[T] = ???
    implicit def mpostToJson(implicit pgs:Serializable[PrivateGroup]], 
                                      cmts:Serializable[List[MPostComment]]) = 
      new Serializable[MPost] {
        def serialize(mpost: MPost): JObject = {
          val privateGroupJSon = pgs.serialize(mpost.privateGroup)
          // make the mpost json with privateGroupJSon which would be empty
          ???
        }
    }
    
    // later where you need to serialize without the inner content:
    implicit val privateGroupToJson = doNothingSerializer[PrivateGroup]
    implicit val mpostCommentsToJson = doNothingSerializer[List[MPostComment]]
    implicitly[Serializable[MPost]].serialize(mpost)
    

    You would need to define default serializable instances in a trait that is then inherited (so that low priority implicits are in scope).

    Note that I'm assuming that the trait for Serializable is:

    trait Serializable[T] {
      def serialize(t: T): JObject
    }
    

    (no [T] method type argument and returns a JObject)