/ / Lecture de json absents sensibles à la lecture - json, scala, playframework, types de données algébriques, play-json

Lecture json lecture absente - json, scala, playframework, types de données algébriques, play-json

J aimerais avoir un lit comme Reads[Patch[T]] pour une classe de cas comme celle-ci

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]

où une valeur json manquante se lit Ignore, une valeur null json se lit Delete et une valeur actuelle valide lit à Patch.

Est-il possible de mettre en place un Reads comme ça?

Json4s a un JNothing type, est-ce que play json a un moyen d’obtenir la même fonctionnalité (je sais qu’il n’ya pas de type rien sous JsValue)?

Edit: pour un contexte sur la manière dont cela pourrait être utilisé, voir le json merge patch rfc.

Réponses:

0 pour la réponse № 1

Laissant de côté les discussions pour savoir si Patch[Nothing] C'est une bonne idée, si nous utilisons cette famille d'objets:

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]

Nous pouvons obtenir le comportement souhaité en implémentant une classe wrapper:

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

Nous devons le faire sinon nous perdrions la distinction de la plus haute importance entre un null valeur et complètement manquant patch.

Maintenant nous pouvons écrire un Reads pour un PatchContainer[T] aussi longtemps que nous fournissons un Reads[T] (par exemple pour un String ou Int etc):

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

}

Le "truc" consiste à détecter s’il existe un patch clé dans l'objet (en utilisant keys.contains), pour obtenir le désir Delete contre Ignore comportement.

Exemples d'utilisation:

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)

et

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

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

et

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

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