5

I have a json with some fields and I want to check if some of them are present. I'm extracting the value and testing it against JNothing, but it is too verbose:

val json: JValue = ...

val jsonIsType1 = (json \ "field1") != JNothing && (json \ "field2") != JNothing

Is there a more compact way to check the presence of a field in a json object using json4s/lift-json? Ideally something like:

val jsonIsType1 = json.has("field1") && json.has("field2")
douglaz
  • 1,306
  • 2
  • 13
  • 17

2 Answers2

21

JValue doesn't have a 'has' operator, but the power of Scala's implicits allows you to add that functionality without too much trouble.

Here's an example of that:

implicit class JValueExtended(value: JValue) {
  def has(childString: String): Boolean = {
    (value \ childString) != JNothing
  }
}

Usage example:

scala> val json = Json.parse("""{"field1": "ok", "field2": "not ok"}""")

scala> json.has("field1")
res10: Boolean = true
Michael Mior
  • 28,107
  • 9
  • 89
  • 113
Damiya
  • 824
  • 6
  • 12
  • This is close to what I'm looking for. – douglaz Feb 11 '14 at 00:55
  • 1
    Love it. Let me add some conciseness: implicit class JValueExtended(value: JValue) { def has(childString: String): Boolean = (value \ childString) != JNothing } – Germán Bouzas Dec 04 '15 at 10:07
  • 1
    Years have passed but this just was very useful, thanks! How would I have known that the != needed to check for 'JNothing' specifically, had I not found this post? – Vorrin Jul 17 '18 at 14:49
2

You can also combine multiple read/validation steps inside a for comprehension. Following are two functions, one which returns an Option[_] and one which returns a Boolean. The first one allows to also work with the data, while the latter only does validation.

import org.json4s.jackson.JsonMethods._

val text =
  """
    |{
    |  "foo": "bar",
    |  "baz": "fnord",
    |  "qux": true
    |}
  """.stripMargin

val json = parse(text)

def readFoo(x: JValue): Option[(String, String, Boolean)] = for {
  JObject(_) <- x.toOption
  JString(foo) <- (x \ "foo").toOption
  JString(baz) <- (x \ "baz").toOption
  JBool(qux) <- (x \ "qux").toOption
  if (qux == true)
} yield (foo, baz, qux)

def validateOnly(x: JValue): Boolean = (for {
  JObject(_) <- x.toOption
  JString(foo) <- (x \ "foo").toOption
  JString(baz) <- (x \ "baz").toOption
  JBool(qux) <- (x \ "qux").toOption
  if (qux == true)
} yield true) getOrElse false

println(readFoo(json))            // Some((bar,fnord,true))
println(readFoo(json).isDefined)  // true
println(validateOnly(json))       // true
Stefan Ollinger
  • 1,577
  • 9
  • 16
  • Very nice solution, but it is a bit overkill if I just want to know if a field exists without asserting other properties like the type of the field. – douglaz Feb 11 '14 at 00:53
  • Great way to unwrap the different elements using for-comprenhensions! – pianista Sep 07 '16 at 18:01