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 № 1Laissant 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),)