pythonjsonscalajackson

Parse JSON in scala and do get with deault value


Sample JSON read from file:

{
  "category": "[{\"a\":3,\"b\":11,\"c\":86}]",
  "ids": "[\"1234\", \"5678\"]",
  "uid": "55555",
  "flag": true
}

Current Code:

val filePath = args(0)
val jsonFileString = Source.fromFile(filePath).getLines.mkString.stripMargin
val mapper = new ObjectMapper().registerModules(DefaultScalaModule)
val jsonStringObj = mapper.readTree(jsonFileString)
val ids = jsonStringObj.get("ids").asText()

This works fine to get me the ids when the json file contains it, but I want to provide a default value in case the key "ids" is not present in the JSON file. How do I do that?

In python I could do something like json_dict.get('ids', 'default_value'). I am looking for an equivalent.


Solution

  • You have to cover 4 cases:

    Many ways of solving this:

    val ids = Option(jsonString.get("ids")) // JsonNode.get return null if the field is not present
      .filterNot(_.isNull)                  // if the filter is present but the value is null it returns a JsonNodeType.NULL
      .map(_.asText())                      // transform to text
      .getOrElse("default-value")           // if the Option is None we get a default value
    
    val idsNode = mapper
      .readTree(jsonFileString)
      .get("ids") // this returns null if field is not present
    
    val ids = 
      if(idsNode == null || idsNode.isNull) // if field is not present or it's present but value is null
        "default value"                     // if true get default value
      else idsNode.asText()                 // if false get the value of the field
    

    from javadoc of path method

    This method is similar to get(String), except that instead of returning null if no such value exists (due to this node not being an object, or object not having value for the specified field), a "missing node" (node that returns true for isMissingNode) will be returned. This allows for convenient and safe chained access via path calls.

    val idsNode = mapper
      .readTree(jsonFileString)
      .path("ids") // this returns `MissingNode` if field is not present
    
    val ids =
      if(idsNode.isNull || idsNode.isMissingNode) // check if the node is null or missing
        "default value"                           // defualt value
      else idsNode.asText()                       // value from the json
    
    // define a case class outside the block of the JsonMapper
    case class IdsWrapper(ids: Option[String])
    
    val mapper = JsonMapper
      .builder()
      .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
      .addModule(DefaultScalaModule)
      .build()
    
    val idsNode = mapper
      .readValue[IdsWrapper](jsonFileString)
      .ids
      .getOrElse("default value")
    

    jackson is a java library to serialize/deserialize json. jackson-module-scala is an extension of that library. It's not exactly the most popular library in the scala ecosystem. So, if you are not restricted to that library, I would suggest to consider other ones such as the ones listed below:

    val ids = ujson.read(jsonFileString)("ids")
      .strOpt
      .getOrElse("default value")
    
    val ids = (Json.parse(jsonFileString) \ "ids")
      .asOpt[String]
      .getOrElse("default value")