How setup spray-json to set null when json element is not present?
Asked Answered
S

2

6

Here is spray-json example. Here is NullOptions trait.

The problem is when I declare a case class say

object MyJsonProtocol extends DefaultJsonProtocol  {
  implicit val some: RootJsonFormat[Some] = jsonFormat2(Some)
}

case class Some (
                name:String,
                age:Int
                )

and json do not contains a field for example:

{
    "name":"John"
}

I get: java.util.NoSuchElementException: key not found: age

So I have to add an Option and NullOption trait like that:

object MyJsonProtocol extends DefaultJsonProtocol with NullOptions  {
  implicit val some: RootJsonFormat[Some] = jsonFormat2(Some)
}

case class Some (
                name:String,
                age:Option[Int]
                )

Everything works. But I do not want to have a case classes where all member are Option. Is there a way to configure spray json unmarshalling to just set nulls without additional Option type?

P.S.

I understand that in general Option is better then null check, but in my case it is just monkey code.

Also complete example of marshalling during response processing is here

Specification answered 19/1, 2015 at 6:51 Comment(0)
F
2

The only way I can think of is to implement your own Protocol via read/write, which might be cumbersome. Below is a simplified example. Note that I changed the age to be an Integer instead of an Int since Int is an AnyVal, which is not nullable by default. Furthermore, I only consider the age field to be nullable, so you might need to adopt as necessary. Hope it helps.

 case class Foo (name:String, age: Integer)

 object MyJsonProtocol extends DefaultJsonProtocol {
    implicit object FooJsonFormat extends RootJsonFormat[Foo] {
      def write(foo: Foo) =
        JsObject("name" -> JsString(foo.name),
                 "age"  -> Option(foo.age).map(JsNumber(_)).getOrElse(JsNull))

      def read(value: JsValue) = value match {
        case JsObject(fields) =>
          val ageOpt: Option[Integer] = fields.get("age").map(_.toString().toInt) // implicit conversion from Int to Integer
          val age: Integer = ageOpt.orNull[Integer]
          Foo(fields.get("name").get.toString(), age)
        case _ => deserializationError("Foo expected")
      }
    }
  }

  import MyJsonProtocol._
  import spray.json._

  val json = """{ "name": "Meh" }""".parseJson
  println(json.convertTo[Foo]) // prints Foo("Meh",null)
Fashionable answered 19/1, 2015 at 11:17 Comment(0)
L
1

It seems you're out of luck

From the doc you linked:

spray-json will always read missing optional members as well as null optional members as None

You can customize the json writing, but not the reading.

Leiker answered 19/1, 2015 at 9:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.