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
When serializing to JSON, if a value of a property in the data object is null, then it will be serialized as a JSON null. Non-existence of a property from the JSON data maps to an unset attribute in the data object space. If the property in the data object is not set (unset), then the property will not appear in the JSON data.
Handling JSON null and empty arrays and objects 1 Null values. JSON has a special value called null which can be set on any type of data including arrays, objects, number and boolean types. 2 Unset property. Non-existence of a property from the JSON data maps to an unset attribute in the data object space. ... 3 Empty property
JSON data has the concept of null and empty arrays and objects. This section explains how each of these concepts is mapped to the data object concepts of null and unset. JSON has a special value called null which can be set on any type of data including arrays, objects, number and boolean types.
The Parse JSON step with the above schema returns "Error - Expected String, received null" Replacing the EndDate data type "string" with "any" causes it to be unreadable in following steps (as though it was never parsed) and changing it to "data" causes an internal server error.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With