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)

2 Answers 2

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)
Sign up to request clarification or add additional context in comments.

4 Comments

can you please copy your answer to this question: stackoverflow.com/questions/25200005/… I have opened bounty there
I can do that, but seriously I do not think it's worthwhile for you to "pay" 300 reputation for the answer.
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)
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.
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))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.