0
votes

I have the following Json block that I have returned as a JsObject

{
  "first_block": [
    {
      "name": "demo",
      "description": "first demo description"
    }
  ],
  "second_block": [
    {
      "name": "second_demo",
      "description": "second demo description",
      "nested_second": [
        {
          "name": "bob",
          "value": null
        },
        {
          "name": "john",
          "value": null
        }
      ]
    }
  ]
}

From this, I want to return a list of all the possible values I could have in the second block, nested array for name and value. so with the example above List([bob,null],[john,null]) or something along those lines.

The issue I am having is with the value section understanding null values. I've tried to match against it and return a string "null" but I can't get it to match on Null values.

What would be the best way for me to return back the name and values in the nested_second array.

I've tried using case classes and readAsNullable with no luck, and my latest attempt has gone along these lines:

val secondBlock = (jsObj \ "second_block").as[List[JsValue]]

secondBlock.foreach(nested_block => {
  val nestedBlock = (nested_block \ "nested_second").as[List[JsValue]]
  nestedBlock.foreach(value => {
    val name = (value \ "name").as[String] //always a string
    var convertedValue = ""
    val replacement_value = value \ "value"
    replacement_value match {
      case JsDefined(null) => convertedValue = "null"
      case _ => convertedValue = replacement_value.as[String]
    }

    println(name)
    println(convertedValue)
  })
}
)

It seems convertedValue returns as 'JsDefined(null)' regardless and I'm sure the way I'm doing it is horrifically bad.

2

2 Answers

0
votes

With play-json I use always case-classes!

I simplified your problem to the essence:

import play.api.libs.json._

val jsonStr = """[
        {
          "name": "bob",
          "value": null
        },
        {
          "name": "john",
          "value": "aValue"
        },
        {
          "name": "john",
          "value": null
        }
      ]"""

Define a case class

case class Element(name: String, value: Option[String])

Add a formatter in the companion object:

object Element {
  implicit val jsonFormat: Format[Element] = Json.format[Element]
}

An use validate:

Json.parse(jsonStr).validate[Seq[Element]] match {
  case JsSuccess(elems, _) => println(elems)
  case other => println(s"Handle exception $other")
}

This returns: List(Element(bob,None), Element(john,Some(aValue)), Element(john,None))

Now you can do whatever you want with the values.

0
votes

Replace JsDefined(null) with JsDefined(JsNull).

You probably got confused, because println(JsDefined(JsNull)) prints as JsDefined(null). But that is not, how null value of a JSON field is represented. null is represented as case object JsNull. This is just a good API design, where possible cases are represented with a hierarchy of classes:

enter image description here