[json4s]:Extracting Array of different objects
Asked Answered
M

1

3

I am using the facebook graph API and the responses look similar to this:

{
  "data": [
    {
      "id": "311620272349920_311718615673419", 
      "from": {
        "id": "1456046457993048", 
        "name": "Richard Ettinson"
      }, 
      "to": {
        "data": [
          {
            "id": "311620272349920", 
            "name": "Barbara Fallerman"
          }
        ]
      }, 
      "with_tags": {
        "data": [
          {
            "id": "311620272349920", 
            "name": "Barbara Fallerman"
          }
        ]
      }, 
      "message": "I was gong out with her", 
      "actions": [
        {
          "name": "Comment", 
          "link": "https://www.facebook.com/311620272349920/posts/311718615673419"
        }, 
        {
          "name": "Like", 
          "link": "https://www.facebook.com/311620272349920/posts/311718615673419"
        }
      ]
}

I managed to for example extract the from field through

val extracted = (json \ "data" \"from").extract[PostFrom]

But I worry that if I use this technique I will need to pass over the Json multiple times to extract all the values I need which could lead to a bad performance.

How exactly could I extract these fields into case classes from the array of non similar objects?

I tried with the following case classes:

abstract class BaseResponse()
case class Data(list:List[Post])
case class Post(id: String, post: PostFrom) extends BaseResponse
case class PostFrom(id: String, name:String)

Which always lead to an empty List, is there a way to get back a Data class which has a list of certain classes which I am interested in? (As example the top level id, from and with_tags)

Montes answered 31/8, 2014 at 12:13 Comment(3)
"which could lead to a bad performance" - You shouldn't be too concerned with the performance until you need to. i.e. if it can iterate through the json a hundred times a millisecond it's probably not going to matter much if it iterates over it once or four timesItching
In general I would agree, but I feel more like I don't fully master how to parse those more complex json constructs with json4s, which in general would be quiet helpful. So I am hopefull someone can give me a better solutionMontes
FWIW doing things like json \ "data" \ "from" is basically a map lookup. the penalty for that is not high until you have to do it 1000s of times. The thing that would negatively affect performance is the number of elements at each level. Which is very small. So yes it's not constant time lookups but with the amount of elements at each level, the question is does it matter?Tumble
M
5

A possibility I found was to use more case classes instead of inheritance:

case class Root[T](data:Option[T])
case class Post(id: String, from: From, message: String)
case class From(id: String, name:String)

Basically there has to be a root object which takes some kind of graphs response object, additionally it is optional so that it won't throw an exception if there was a problem with the parsing of the response.

I then used it in the following way:

val body = r.entity.asString
val json = parse(r.entity.asString)
val root = json.extract[Root[Post]]

root.data match {
  case Some(post) =>
      val tagger = Tagger(post.from.id, post.from.name, post.id, post.message)
      log.info(s"We received $tagger")
      originalSender ! RetrievedTagger(tagger)

  case None => originalSender ! NoTaggerFound
}
Montes answered 5/9, 2014 at 11:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.