3

I'm using json4s library in my project to manually parse JSON field by field (without automatic deserializing it to case classes).

For example I have following JSON:

{
  result: "OK",
  persons: [
    {name: "Alex"},
    {name: null}
  ] 
}

And the official guide suggest to use this approach to manually parse it:

for {
         JArray(persons) <- json / "persons"
         JObject(person) <- persons
         JField("name", JString(name)) <- person
       } yield Person(name) 

The problem is that this for-comprehension skips persons which has null names. I think this is because in for-comprehension I used JString(name), so it expects some String value, not a null.

Is there any way to solve this? I just want to iterate over array and visit every object (even if it has null instead some String)

WelcomeTo
  • 19,843
  • 53
  • 170
  • 286

2 Answers2

2

Yeah, you've already got the reason it can't work, you just have to change the pattern match like the following.

case class Person(name: String)
// the result is actually List[Option[Person]]
val result =
  for {
    JArray(persons) <- json \ "persons"
    JObject(person) <- persons
    // jv is JsonAST.JValue
    JField("name", jv) <- person
  } yield {
    // in case of match error so add _ here
    jv match {
      case JString(name) => Some(Person(name))
      case JNull => Some(Person(null))
      case _ => None
    }
  }

Actually, there is another simpler way following the Extracting values part in the documentation you mentioned.

case class Person(name: String)
import org.json4s.DefaultFormats
implicit val formats = DefaultFormats // Brings in default date formats etc
val result2 = (json \ "persons").extract[List[Person]]
// List(Person(Alex), Person(null))
println(result2)
Allen Chou
  • 1,229
  • 1
  • 9
  • 12
  • can you please copy your answer to this question: http://stackoverflow.com/questions/25200005/extract-options-from-potentially-null-json-values-using-for-expression I have opened bounty there – WelcomeTo Sep 07 '15 at 10:44
  • I can do that, but seriously I do not think it's worthwhile for you to "pay" 300 reputation for the answer. – Allen Chou Sep 07 '15 at 10:54
  • And also wanted to ask: each field of my JSON potentially can be null, and I have about 15 fields I'm interested in, so seems I will need to replace each extraction of field with this pattern matching (5 lines instead 1 line of code). Is there some more compact way than pattern matching? 2nd approach is not for me (i'm doing a lot of transformation of extracted fields before creating case class) – WelcomeTo Sep 07 '15 at 10:56
  • Yeah, I get it. So the 15 fields are all in the same type, say JString? And you'd better provide the structure of the json, or it will be hard for others to help you out. – Allen Chou Sep 07 '15 at 11:00
0

This will do,

 val r:List[Person] = for {
    JArray(persons) <- JsonMethods.parse(json) \ "persons"
    JObject(person) <- persons
    //JField("name", JString(name)) <- person
   p = Person(person.head._2) //or Person(person.find(_._1=="name").map(_._2).getOrElse(null))
  } yield p

  println(r) //List(Person("Alex"), Person(null))
S.Karthik
  • 1,389
  • 9
  • 21