Search code examples
jsonscalajiraliftcase-class

Extract nested entities and values from JSON


I am trying to extract Issues from Jira and put them into List[Issue]. I figured out how to download and parse JSON:

val json = JsonParser.parse(content)

I can also extract some numbers in the root of JSON:

val total = (json \ "total").extract[Int]
val maxResults = (json \ "maxResults").extract[Int]
println("Received " + total + " from " + maxResults + " issues")

But when I am trying to extract list of all the issues

val issues = (json \ "issues")
println(issues)
issues.extract[List[Issue]]

I am receiving a error: Exception in thread "main" net.liftweb.json.MappingException: No usable value for id Do not know how to convert JString(13604) into int I don't understand why can't it convert 13604 to Int. Here is my case class:

case class Issue(id: Int,
    key: String,
    summary: String,
    issueTypeName: String,
    resolutionName: Option[String],
    resolutionDate: Option[DateTime],
    timeSpent: Option[Int],
    creatorName: String,
    reporterName: String,
    updated: DateTime,
    created: DateTime,
    priorityName: String,
    description: String,
    dueDate: Option[DateTime],
    statusName: String,
    assigneeName: String,
    projectId: Int,
    projectKey: String,
    projectName: String,
    timeEstimate: Option[Int],
    ownerName: String,
    timeOriginalEstimate: Option[Int]
                    )
  1. Can someone help me with this Int problem?

  2. Moreover, JSON has nested elements for some properties like project has nested id, key and name. Before I extracted issues with json \ "issues" I saw another error - I believe it is because JSON extractor don't know he need to go to the nested elements. How can I let him know about it? I thought I could do something like this:

    for(issue <- issues) { val id = (issue \ "id").extract[Int] println(id) }

And use issue \ "project" \ "id" for nested items, and then create new Case Class object and add it to List var (mutable but I have no idea how to do it in other way). But I receive compile time error:

 Error:(53, 16) value foreach is not a member of net.liftweb.json.JsonAST.JValue
        for(issue <- issues) {
                     ^

I am new to Scala and overall Java infrastructure and frameworks, so I would appreciate for samples of code.


PS. When I changed id to String in my case class I now receive another error:

Exception in thread "main" net.liftweb.json.MappingException: No usable value for summary Did not find value which can be converted into java.lang.String

This is because "summary" is nested into the "fields". So, my second question is still actual: 2. How can I work with nested values?

and new related question: 3. If I want to use Int for id - how can I convert it?


Solution

  • I have found solution myself:

    val issues = (json \\ "issues").children
     for (issue <- issues) {
            val item = new Issue (
                        (issue \ "id").extract[String].toInt,
                        (issue \ "fields" \ "summary").extract[String],
                        (issue \ "fields" \ "issuetype" \ "name").extract[String],
                        (issue \ "fields" \ "resolutiondate").extractOrNone[String] match {
                            case None => None
                            case Some (null) => None
                            case Some (dt) => Some (dateTimeFormatter.withZoneUTC ().parseDateTime (dt))
                        },
            etc...) 
    }
    

    In this case I continue using single case class (the structure I define for my own needs) and parse nested json properties into it, doing types transformations on the fly where needed.