json4s

json4s Custom Serializers

I recently had to work with custom serializers, which were interesting to say the least. I had two case classes and a trait in the following formats:

trait Parent
case class ChildClassOne(kind: String = "first_type", id: String) extends Parent
case class ChildClassTwo(kind: String = "second_type", id: String) extends Parent

And another case class which contained a list of Parents:

case class ParentResponse(total: Int, results: List[Parent])

Basically the json response might have a list of objects which can either be of type ChildClassOne or ChildClassTwo.

Because of this (I thought) I needed to create a custom serializer:

class ParentSerializer extends CustomSerializer[Parent](format => ( {
    case JObject(List(JField("kind", JString(kind)), JField("id", JString(id)))) 
        if kind == "first_type" => ChildClassOne(kind, id) 
    case JObject(List(JField("kind", JString(kind)), JField("id", JString(id)))) 
        if kind == "second_type" => ChildClassTwo(kind, id) 
  }, {
    case _ => null
  }))

This worked fine. Problem was that these objects might get quite big and I didn’t want to specify every single field in custom serializer. I also was not modifying the properties in any way, and was using the custom serializer just to return the right type of case class based on the kind field.

After trying different ways, I found doing it the following way to be the best solution using a Serializer:

trait Parent
case class ChildClassOne(kind: String = "first_type", id: String) extends Parent
case class ChildClassTwo(kind: String = "second_type", id: String) extends Parent

case class ParentResponse(total: Int, results: List[Parent])

class ParentSerializer extends Serializer[Parent] {
    private val ParentClass = classOf[Parent]
    implicit val formats = DefaultFormats

    def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Parent] = {
      case (TypeInfo(ParentClass, _), json) => json match {
        case JObject(JField("kind", JString(kind)) :: _) => kind match {
          case "first_type" => json.extract[ChildClassOne]
          case "second_type" => json.extract[ChildClassTwo]
        }

        case _ => throw new MappingException("Invalid kind")
      }
    }

    def serialize(implicit format: Formats): PartialFunction[Any, JValue] = Map()
  }

implicit val formats = DefaultFormats + new ParentSerializer
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s