Scrivi un valore arbitrario non trovato in una cassa di classe utilizzando i combinatori Scala JSON di Play (2.2)
-
21-12-2019 - |
Domanda
Mi piacerebbe implementare un Writes
che emette un oggetto JSON che non è stato trovato nella classe che viene serializzato.
per la cassa della cassa:
case class Foo(i:Int, s:String)
.
Sto cercando di produrre:
{
"i": <int>,
"s": "<string>",
"other": "Some value."
}
.
Il primo tentativo naïve era:
val writes: Writes[Foo] = ((
(__ \ "i").write[Int] and
(__ \ "s").write[String] and
(__ \ "other").write("Some value.")
)(unlift(Foo.unapply))
.
Naturalmente, che non si compilerà come le successive chiamate and
producono un CanBuild3
e Foo
tagcode di unapply
producono un Tuple2
. Ho guardato nell'aggiunta di un valore al risultato, producendo un Tuple3
, ma quello che ho trovato Sembra piuttosto male e I manutentori della lingua non lo impleniranno .
Ci sono modi per aggirare questo, ma preferirei non inquinare le mie lezioni del modello con questi valori del decoratore che vorrei aggiungere al JSON risultante.
Qualche suggerimento?
Vale la pena notare che può Vai dall'altra direzione, fornendo valori con Reads.pure
per i casi in cui un valore non esiste in JSON ma è specificato dall'oggetto risultante.
Soluzione
Puoi farlo abbastanza diretto dal disugurazione di un po ':
val writes: Writes[Foo] = (
(__ \ "i").write[Int] and
(__ \ "s").write[String] and
(__ \ "other").write[String]
)(foo => (foo.i, foo.s, "Some value."))
.
Il unlift(Foo.unapply)
è solo un modo elegante per ottenere una funzione da un Foo
a una tupla del tipo richiesto dall'espressione precedente del costruttore applicativo, e puoi sostituirlo con la propria funzione che può aggiungere qualsiasi cosa desideri. Se you veramente voleva persino la sintassi più pulita, è possibile utilizzare Sforzo :
import shapeless.syntax.std.tuple._
val writes: Writes[Foo] = (
(__ \ "i").write[Int] and
(__ \ "s").write[String] and
(__ \ "other").write[String]
)(_ :+ "Some value.")
.
È bello, ma può essere eccessivo.
Altri suggerimenti
Un'altra opzione è utilizzare un generatore di oggetti che implementa un unapply
che restituisce i valori extra.Questo rende il pulitore scrive, ma aggiunge un nuovo oggetto.Ho trovato utile anche se sia il metodo applicato e inappropriato può essere utilizzato per ulteriori massaggi di dati nell'oggetto finale (ad esempio: https://stackoverflow.com/a/22504468/1085606 )
Esempio:
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))
.