amanya
amanya

Reputation: 85

How can I parse a json and extract to different case classes depending of its content

I'm trying to parse different types of events in json format using json4s. I have written some case classes to represent the different events all inheriting from a base class Event:

  abstract class Event{ def EventType : String }
  case class StartSession(val EventType: String, val Platform: String) extends Event
  case class AdView(val EventType: String, val EventSubtype: String) extends Event

This is the function I use to parse the StartSession event:

  def parser(json: String): Event = {
    val parsedJson = parse(json)
    val s = parsedJson.extract[StartSession]
    return s
  }

This function will correctly parse a json like {"EventType":"StartSession","Platform":"Portal"}

I am looking for a way to generalize the parser function so I can use it to parse all types of events that inherits from Event and then do pattern matching over the return value of the function.

Upvotes: 5

Views: 1363

Answers (1)

Kulu Limpa
Kulu Limpa

Reputation: 3541

Type hints provide the solution to the problem. If you have a polymorphic type, such as Event, the type hint (here EventType) tells json4s into which actual type a json object should be deserialized.

For reference, have a look at the github page of json4s. There is a section called "Serializing polymorphic Lists".

Since the type hint is only necessary for deserialization, we can get rid of the field EventType in Event.

abstract class Event
case class StartSession(Platform: String) extends Event
case class AdView(EventSubtype: String) extends Event

We need to bring a Formats instance into the scope of extract. The instance tells json4s how to perform deserialization. In our case, we need to specialize typeHintFieldName and typeHints. The first is the key of our type hint, in the example it is "EventType". The latter is a mapping from string values to classes. If we are using the class name as value, then ShortTypeHints will do the trick. Othwerise, we could implement our own, specialized TypeHints.

A concrete solution could look as follows:

def main(args: Array[String]): Unit ={
  val json =
    s"""
       |[
       |  {
       |    "EventType": "StartSession",
       |    "Platform": "Portal"
       |  },
       |  {
       |    "EventType": "AdView",
       |    "EventSubtype": "SpecializedView"
       |  }
       |]
     """.stripMargin
  implicit val formats = new DefaultFormats {
    override val typeHintFieldName: String = "EventType"
    override val typeHints: TypeHints =
      ShortTypeHints(
        List(classOf[StartSession], classOf[AdView])
      )
  }
  val parsedJson = parse(json)
  val s = parsedJson.extract[List[Event]]
  println(s)
}

Upvotes: 3

Related Questions