Search code examples
jsonscalalift-json

Issue with parsing nested JSON values with Lift-JSON


I'm using Scala 2.12 and trying to parse the below JSON file.

{
    "comp1": {
        "metrics": {
            "operation1": {
                "alias": "activity_operation",
                "weight": 10
            },
            "operation2": {
                "alias": "service_operation",
                "weight": 22
            }
        }
    },
    "comp2": {
        "metrics": {
            "operation1": {
                "alias": "activity_operation",
                "weight": 14
            },
            "operation4": {
                "alias": "service_operation",
                "weight": 16
            }
        }
    }
}

I've loaded the json into config variable, defined a case class and trying the below:

  case class OperationDetails(alias: String, weight: Int)

  for (detail <- (config \ "comp1").children) {
    println(detail.extract[OperationDetails])
  }

This gives me the error Exception in thread "main" net.liftweb.json.MappingException: No usable value for alias. Did not find value which can be converted into java.lang.String

I can't use `operation1' and retrieve children as operations are random.

I need to retrieve the operation names operation1, operation2, operation4, .. and their respective aliases and weights. Any ideas?


Solution

  • You are missing at least one level of nesting, and possibly also the implicit val formats.

    This will print all of the operations. Note the conversion into JObject in order to be able to retrieve field names.

      // Setup 
      case class Operation(alias: String, weight: Int)
      implicit val formats = DefaultFormats
    
      // Traversal
      val comps: List[JsonAST.JValue] = config.children
      for (comp <- comps) {
        val metrics:List[JsonAST.JValue] = comp.children
        for (metric <- metrics) {
          val operations:List[JsonAST.JField] = metric.asInstanceOf[JObject].obj
          for (operation <- operations) {
            val op = operation.value.extract[Operation]
            // Do something here
            println(s"${operation.name}:(${op.alias},${op.weight})")
          }
        }
      }
    

    Output:

    operation1:(activity_operation,10)
    operation2:(service_operation,22)
    operation1:(activity_operation,14)
    operation4:(service_operation,16)