2

I am trying to figure out how to avoid null values. My problem is, that I am querying an API and it returns a null value.

Here's an example:

import play.api.libs.json._

case class InnerExample(value: String)

object InnerExample {
def fromJson(json: JsValue) = InnerExample(
    value = (json \ "value")as[String]
}

case class Example(innerValue: Option[InnerExample])

object Example {
def fromJson(json: JsValue) = Example(
    optionalValue = (json \ "innerValue").asOpt[String].map(InnerExample.apply)
    )
}

The problem I am having is that asOpt transforms innerValue not to a None, but to a Some(null). I understand why and it makes a lot of sense. But I need to figure out a way to deal with it. Something more elegant than matching for Some(null). I am very grateful for any ideas.

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
SomeStranger314
  • 327
  • 1
  • 3
  • 12

2 Answers2

3

Consider readNullable. Here is working example:

import play.api.libs.json._

case class InnerExample(value: String)
case class Example(innerValue: Option[InnerExample])

object InnerExample {
  implicit val format = Json.format[InnerExample]
}

object Example {
  implicit val jsonread: Reads[Example] =
    (JsPath \ "innerValue").readNullable[InnerExample].map(Example.apply)
}

object NullableJson extends App {
  val jsonSomeStr = """{ "innerValue": { "value": "woohoo" } }"""
  val jsonSome = Json.parse(jsonSomeStr).as[Example] 
  println(jsonSome) // Example(Some(InnerExample(woohoo)))

  val jsonNullStr = """{ "innerValue": null }"""
  val jsonNull = Json.parse(jsonNullStr).as[Example]
  println(jsonNull) // Example(None)
}
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
  • Thank you for your answer. And please forgive me for taking two days to answer you after asking you for help. There were unexpected circumstances. – SomeStranger314 Jun 10 '18 at 17:25
1

Thanks to the answer from @Mario Galic, I went back to my code and opened up the documentation of JSLookups, which is what I get back when I use method \ of a JsValue. Turns out it does have a way to deal with null-values. It is done like this:

optionalValue = (json \ "innerValue").validateOpt[String].get.map(InnerExample.apply)

I think Mario's way is cleaner and should be recommended, but in my case it would means rewrite 10 case classes and my way is faster in modifying my existing code. Still, that is the reason why I will mark Mario's answer as accepted answer.

SomeStranger314
  • 327
  • 1
  • 3
  • 12