将JSON字段添加到Scala案例类

Ral*_*lph 2 json scala playframework

我有一个Scala案例类

case class NumericParam(minValue: Option[Int] = None,
                        maxValue: Option[Int] = None,
                        decimalPlaces: Option[Int] = None,
                        signed: Option[Boolean] = None,
                        hintText: Option[String] = None)
Run Code Online (Sandbox Code Playgroud)

和它的伴侣对象,我定义了一个隐式writes方法

object NumericParam {
    implicit val writes = new Writes[NumericParam] {
        override def writes(value: NumericParam): JsValue = {
          Json.obj(
            "dataType" -> "Numeric",
            "minValue" -> value.maxValue,
            "maxValue" -> value.maxValue,
            "decimalPlaces" -> value.decimalPlaces,
            "signed" -> value.signed,
            "hintText" -> value.hintText
          )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在添加该字段dataType.有没有办法使用宏派生Writes值(从Json.writes[NumericParam])并只添加额外的dataType字段?

Mic*_*jac 6

你可以Writes#transform用来做.一种方法是使用一个函数JsValue => JsValue.安全的方式:

implicit val writes = Json.writes[NumericParam] transform { js =>
  js match {
    case obj: JsObject => obj + ("dataType" -> JsString("Numeric"))
    case _ => js
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,我们真的知道js应该始终是a JsObject,因为我们在特定类型上运行,所以我们可以缩短它.

implicit val writes = Json.writes[NumericParam]
   .transform(_.as[JsObject] + ("dataType" -> JsString("Numeric")))
Run Code Online (Sandbox Code Playgroud)

例:

scala> val num = NumericParam(Some(1), Some(10), Some(2), Some(false), Some("test"))
num: NumericParam = NumericParam(Some(1),Some(10),Some(2),Some(false),Some(test))

scala> Json.toJson(num)
res5: play.api.libs.json.JsValue = {"minValue":1,"maxValue":10,"decimalPlaces":2,"signed":false,"hintText":"test","dataType":"Numeric"}
Run Code Online (Sandbox Code Playgroud)

为了使上面的类型更安全通用,我们可以使用一些隐式魔术来扩展OWrites(总是写入JsObject).

implicit class OWritesExt[A](owrites: OWrites[A]) {

  /** Add a (key, value) pair to the JSON object,
   *  where the value is constant.
   */
  def withConstant[B : Writes](key: String, value: B): OWrites[A] = 
    withValue(key, _ => value)

  /** Add a key, value pair to the JSON object that is written, where
   *  the value depends on the original object.
   */
  def withValue[B : Writes](key: String, value: A => B): OWrites[A] = 
    new OWrites[A] {
      def writes(a: A): JsObject = owrites.writes(a) ++ Json.obj(key -> value(a))
    }

}
Run Code Online (Sandbox Code Playgroud)

用法:

implicit val writes = Json.writes[NumericParam]
    .withValue("type", _.getClass.toString)
    .withConstant("other", "something")

scala> Json.toJson(NumericParam(Some(1)))
res6: play.api.libs.json.JsValue = {"minValue":1,"type":"class NumericParam","other":"something"}
Run Code Online (Sandbox Code Playgroud)

现在你可以废弃一些原始样板,并将这样的链调用一起.现在我只是想知道为什么我一直没有这样做.