Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How To Provide DefaultValues For Fields In JSON While Marshalling Or Unmarshalling? #257

Open
shankarshastri opened this issue May 2, 2018 · 4 comments

Comments

@shankarshastri
Copy link
Contributor

shankarshastri commented May 2, 2018

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
Copy link

ramanmishra commented May 8, 2018

#56
did you checked this?

@ehsanshah
Copy link

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
Copy link
Contributor Author

@ehanshah, there's a PR, but we need to wait till it get merged.
#93

@Daenyth
Copy link

Daenyth commented Feb 6, 2019

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
                              ))
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants