Écrivez une valeur arbitraire introuvable dans une classe de cas à l'aide des combinateurs Scala JSON de Play (2.2)
-
21-12-2019 - |
Question
J'aimerais mettre en œuvre un Writes
qui émet un objet JSON introuvable dans la classe en cours de sérialisation.
Pour la classe de cas :
case class Foo(i:Int, s:String)
Je cherche à produire :
{
"i": <int>,
"s": "<string>",
"other": "Some value."
}
La première tentative naïve était la suivante :
val writes: Writes[Foo] = ((
(__ \ "i").write[Int] and
(__ \ "s").write[String] and
(__ \ "other").write("Some value.")
)(unlift(Foo.unapply))
Naturellement, cela ne sera pas compilé comme suit and
les appels produisent un CanBuild3
et Foo
c'est unapply
produit un Tuple2
.J'avais envisagé d'ajouter une valeur au résultat, produisant ainsi un Tuple3
, mais ce que j'ai trouvé ça a l'air plutôt mauvais et le les responsables du langage ne l'implémenteront pas.
Il existe des moyens de contourner ce problème, mais je préfère ne pas polluer mes classes de modèles avec ces valeurs de décorateur que j'aimerais ajouter au JSON résultant.
Aucune suggestion?
Ça vaut la peine de te noter peut aller dans l'autre sens, en fournissant des valeurs avec Reads.pure
pour les cas où une valeur n'existe pas dans JSON mais est spécifiée par l'objet résultant.
La solution
Vous pouvez le faire assez simplement en désucrant un peu :
val writes: Writes[Foo] = (
(__ \ "i").write[Int] and
(__ \ "s").write[String] and
(__ \ "other").write[String]
)(foo => (foo.i, foo.s, "Some value."))
Le unlift(Foo.unapply)
est juste une façon sophistiquée d'obtenir une fonction d'un Foo
à un tuple du type requis par l'expression du générateur applicatif précédente, et vous pouvez le remplacer par votre propre fonction qui peut ajouter ce que vous voulez.
Si tu vraiment vous vouliez une syntaxe encore plus propre, vous pouvez utiliser Informe:
import shapeless.syntax.std.tuple._
val writes: Writes[Foo] = (
(__ \ "i").write[Int] and
(__ \ "s").write[String] and
(__ \ "other").write[String]
)(_ :+ "Some value.")
C'est beau, mais c'est peut-être exagéré.
Autres conseils
Une autre option consiste à utiliser un générateur d'objets qui implémente un unapply
qui renvoie les valeurs supplémentaires.Cela rend les écritures plus propres, mais ajoute un nouvel objet.J'ai trouvé cela utile, car les méthodes d'application et de désapplication peuvent être utilisées pour un massage supplémentaire des données dans l'objet final (par exemple : https://stackoverflow.com/a/22504468/1085606)
Exemple:
case class Foo(i: Int, s: String)
object FooBuilder {
def unapply(foo: Foo): Option[(Int, String, String)] = {
Some((foo.i, foo.s, "Some extra value"))
}
}
val writes: Writes[Foo] = ((
(__ \ "i").write[Int] and
(__ \ "s").write[String] and
(__ \ "other").write[String]
)(unlift(FooBuilder.unapply))