4

I want to use ujson of the upickle library to extract an optional string from a json document. In some documents the json field exists, in others not.

When acessing the field and the field does not exist I get a NoSuchElementException:

val json = ujson.read(jsonString)
json("attributename").str

results in: java.util.NoSuchElementException: key not found: attributename

What is the idiomatic way to deal with optional json attributes in ujson?

nemoo
  • 3,269
  • 4
  • 38
  • 51

5 Answers5

4

If you want to return default value in case of any exception you can use Try with getOrElse:

val result = Try(json("attributename").str).getOrElse("defaultValue")

The result will be value of attributename key or defaultValue string if there is no such key.

Duelist
  • 1,562
  • 1
  • 9
  • 24
2

I think the idiomatic way is to have a case class instead of going against the JSON AST manually.

In your case class you could then have an Option[String] field.

case class MyModel( attributeName: Option[String] )
implicit val rw: ReadWriter[MyModel] = macroRW

read[MyModel](jsonString)

But from the looks of it, you could do

json.obj.value.get("attributename").map(_.str)

to get an Option[String] back.

Thilo
  • 257,207
  • 101
  • 511
  • 656
2

Don't forget, a json is an object.

So we can check it like this:

val json = ujson.read(jsonString)
if (json.obj.get("attributename").nonEmpty) {
  json("attributename").str
  ...
}

And btw, you can get the keySet like this:

json.obj.keySet
JavaNoScript
  • 2,345
  • 21
  • 27
1

According to liahoy

the canonical way is to add None defaults

for example,

import upickle.default._

case class User(name: String, age: Option[Int] = None)
implicit val userRW: ReadWriter[User] = macroRW
read[User]("""{ "name": "Picard" }""")

outputs

res0: User = User(Picard,None)
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
0

I am not sure, but, I was looking the GitHub repo here It seems at line 62 and from line 87 till line 99, it just call x.obj(i).

It doesn't perform any check, and just call it. This lead to a java.util.NoSuchElementException because trying to access without a check. I didn't see around any version to get the Option, or to even perform a check if this value exists.

I would suggest to go via a Try/Success/Failure idiom on scala

val tryAttr = Try{json("attributename").str}
tryAttr match {
  case Success(_) => doYourThing
  case Failure(t: NoSuchElementException) => DoSomethingElse
}
Tizianoreica
  • 2,142
  • 3
  • 28
  • 43