How To Provide DefaultValues For Fields In JSON While Marshalling Or Unmarshalling?
shankarshastri opened this issue · 4 comments
shankarshastri commented
I want to have default value for x2 which is optional in my request payload.
import spray.json._
import DefaultJsonProtocol._
case class X(x1: Int, x2: Option[Int])
object XApply {
def apply(x1: Int, x2: Option[Int] = Some(10)): X = X(x1, x2)
}
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val XFormat = jsonFormat2(X)
}
import MyJsonProtocol._
"""{"x1":2 }""".parseJson.convertTo[X] // But looks like this will assign x2 to None
https://scastie.scala-lang.org/shankarshastri/kM9yeuDgSMqstJkqS91Lvw
Any suggestions?
@ktoso @jrudolph
ramanmishra commented
#56
did you checked this?
ehsanshah commented
hi how can use default parameters for case class spray Json ?
for example case class Person(name: String,family:String, age: Int, mySelf: Option[String]=Some(generateID)) when i fill case class with json i have got mySelf=None
shankarshastri commented
Daenyth commented
Here's a workaround for now:
import spray.json._
/** JsonFormat that offers backwards compatibility for newly-added fields
* by splicing in a default value when the new field key is not present in the json.
*
* This is important because spray does not support reading case class default arguments in all case.
* Without using this, reading old-format messages (for example from an external queue), will throw an exception.
*
* Currently, spray-json *will* use `= None` default values, so this class does not need to be used with `Option` fields.
*
* @see [[https://github.com/spray/spray-json/pull/93 Unmerged spray-json PR adding support for default arguments]]
*
* @example {{{
* case class Person(name: String, age: Int, newTagsField: Map[String, String] = Map.empty)
* object Person {
* implicit val sprayFormatForPerson: RootJsonFormat[Person] = {
* import JsonProtocol._
* MissingFieldFormat(jsonFormat3(Person.apply),
* "newTagsField" -> Map.empty[String, String])
* }
* }
* }}}
*/
class MissingFieldFormat[A](realFormat: RootJsonFormat[A],
defaults: Map[String, JsValue])
extends RootJsonFormat[A] {
override def read(json: JsValue): A = {
val fixedJson = try {
JsObject(defaults ++ json.asJsObject.fields)
} catch {
case _: DeserializationException =>
// Was not a JsObject, return and let it fail later with better error
json
}
realFormat.read(fixedJson)
}
override def write(obj: A): JsValue = realFormat.write(obj)
}
object MissingFieldFormat {
def apply[A, B: JsonWriter](
realFormat: RootJsonFormat[A],
default: (String, B)
): MissingFieldFormat[A] =
new MissingFieldFormat[A](realFormat, Map(default._1 -> default._2.toJson))
def apply[A, B: JsonWriter, C: JsonWriter](
realFormat: RootJsonFormat[A],
defaultB: (String, B),
defaultC: (String, C)
): MissingFieldFormat[A] =
new MissingFieldFormat[A](realFormat,
Map(
defaultB._1 -> defaultB._2.toJson,
defaultC._1 -> defaultC._2.toJson
))
}