播放json缺席敏感的读取

时间:2018-01-07 21:42:24

标签: json scala playframework algebraic-data-types play-json

对于像这样的案例类,我希望有一个类似Reads[Patch[T]]的读取

sealed trait Patch[+T]
case class Update[+T](value: T) extends Patch[T]
case object Delete extends Patch[Nothing]
case object Ignore extends Patch[Nothing]

缺少的json值读取到Ignore,空的json值读取到Delete,有效的当前值读取到Patch

是否可以像这样实施Reads

Json4s有一个JNothing类型,玩json有没有办法实现相同的功能(我知道JsValue下没有任何类型的东西)?

编辑:有关如何使用此内容的背景信息,请参阅json merge patch rfc

1 个答案:

答案 0 :(得分:0)

如果我们使用这个对象系列,暂且不讨论Patch[Nothing]是否是一个好主意:

sealed trait Patch[+T]
case class Update[+T](value: T) extends Patch[T]
case object Delete extends Patch[Nothing]
case object Ignore extends Patch[Nothing]

我们可以通过实现包装类来获得所需的行为:

case class PatchContainer[T](patch: Patch[T])

我们必须这样做,否则我们会失去null值与完全缺失patch之间的重要区别。

现在,只要我们提供合适的Reads(例如PatchContainer[T]Reads[T]等),我们就可以为StringInt

class PatchContainerJson[T](implicit val rdst:Reads[T]) {

  implicit val patchContainerReads = new Reads[PatchContainer[T]] {

    override def reads(json: JsValue): JsResult[PatchContainer[T]] = {
      json.validate[JsObject].map { obj =>
        (obj \ "patch").asOpt[T].fold[PatchContainer[T]] {
          if (obj.keys.contains("patch")) {
            PatchContainer(Delete)
          } else {
            PatchContainer(Ignore)
          }
        } { v =>
          PatchContainer(Update(v))
        }
      }
    }
  }

}

这里的“技巧”是检测对象中是否有patch个密钥(使用keys.contains),以获得所需的Delete vs Ignore行为。< / p>

用法示例:

scala> import play.api.libs.json._

scala> val json = Json.parse(""" { "patch": 42 } """ )
json: play.api.libs.json.JsValue = {"patch":42}

scala> val pcti = new PatchContainerJson[Int]()

scala> import pcti._

scala> val result = json.validate[PatchContainer[Int]]
result: play.api.libs.json.JsResult[models.PatchContainer[Int]] = JsSuccess(PatchContainer(Update(42)),)

scala> result.get.patch
res0: models.Patch[Int] = Update(42)

...
scala> val ignoredJson = Json.parse(""" { } """) 

scala> ignoredJson.validate[PatchContainer[Int]]
res1: play.api.libs.json.JsResult[models.PatchContainer[Int]] = JsSuccess(PatchContainer(Ignore),)

scala> val deleteJson = Json.parse(""" { "patch": null } """)

scala> deleteJson.validate[PatchContainer[Int]]
res2: play.api.libs.json.JsResult[models.PatchContainer[Int]] = JsSuccess(PatchContainer(Delete),)