2013-02-23 8 views
14

Sto provando a serializzare/deserializzare alcune case classes a/da Json ... e ho problemi quando si tratta di case classes con un solo campo (sto usando Giocare 2.1):Come serializzare/deserializzare le classi di casi da/a Json in Play 2.1

import play.api.libs.json._ 
import play.api.libs.functional.syntax._ 

case class MyType(type: String) 

object MyType { 

    implicit val myTypeJsonWrite = new Writes[MyType] { 
    def writes(type: MyType): JsValue = { 
     Json.obj(
     "type" -> MyType.type 
    ) 
    } 
    } 

    implicit val myTypeJsonRead = (
    (__ \ 'type).read[String] 
)(MyType.apply _) 
} 

il codice di cui sopra sempre genera il seguente messaggio di errore:

[error] /home/j3d/Projects/test/app/models/MyType.scala:34: overloaded method value read with alternatives: 
[error] (t: String)play.api.libs.json.Reads[String] <and> 
[error] (implicit r: play.api.libs.json.Reads[String])play.api.libs.json.Reads[String] 
[error] cannot be applied to (String => models.MyType) 
[error]  (__ \ 'method).read[String] 
[error]      ^

lo so ... una classe case che contiene solo una stringa non ha molto senso ... ma ho bisogno di serializzare/deserializzare una classe di case molto simile a quella descritta sopra che proviene da una esterna l biblioteca.

Qualche idea? Mi sto perdendo qualcosa? Qualsiasi aiuto sarebbe molto apprezzato ... sto diventando pazzo :-(Grazie.

+1

fare riferimento http://stackoverflow.com/a/20130414/1435971 per esempio per convertire JSON a Scala caso classe – prarthan

risposta

23

JSON combinatori non funziona per singola classe caso campo in Play 2.1 (dovrebbe essere possibile in 2.2)

Pascal (autore di questa API) ha spiegato questa situazione qui https://groups.google.com/forum/?fromgroups=#!starred/play-framework/hGrveOkbJ6U

ci sono alcune soluzioni che funziona, come questo:

case class MyType(value: String) 
val myTypeRead = (__ \ 'value).read[String].map(v => MyType(v)) // covariant map 

ps: type è una parola chiave in Scala, non può essere usato come nome parametro (ma io a ssume è solo per questo esempio)

modifica: questa soluzione alternativa non è ancora richiesta con la riproduzione 2.3.X. La macro funziona bene.

+2

È possibile catturare 'type' in un caso di classe di serializzazione JSON avvolgendo il nome del campo in apici inversi .Non riesco a capire come mostrarlo nel markdown perché i backtick sono i blocchi di codice :) –

+0

questo dovrebbe funzionare: '\' tipo \ '' - basta sfilarli con una barra rovesciata, come questa: '\' \\ 'type \\' \' ' –

+3

Sto usando Play 2.3 (Scala) e ho anche bisogno di lavorare su questa limitazione. Non penso sia ancora risolto. –

4

Il problema è che (per quanto ne so io) il framework Play 2.1 gestisce solo le tuple a partire da Tuple2. Negli esempi viene utilizzato in questo modo:

case class CaseClass(key1: String, key2: String) 
object CaseClass { 
    implicit val caseClassFormat = { 
    val jsonDescription = 
     (__ \ "key1").format[String] and (__ \ "key2").format[String] 

    jsonDescription(CaseClass.apply _, unlift(CaseClass.unapply)) 
    } 
} 

E poi usarlo

val caseClassJson = Json.toJson(CaseClass("value1", "value2")) 

println(caseClassJson) 
println(Json.fromJson[CaseClass](caseClassJson)) 

Nel tuo caso non è possibile utilizzare il metodo and (si dispone di un solo valore) e quindi ottenere accesso a quella bella funzione apply di FunctionalBuilder#CanBuildX (dove X è da 1 a 22).

Al fine di fornire qualche cosa di simile si può creare una classe implicita che fornisce un metodo build con una firma simile a quella bella metodo apply

implicit class FormatBuilder[M[_], A](o: M[A]) { 
    def build[B](f1: A => B, f2: B => A)(implicit fu: InvariantFunctor[M]) = 
    fu.inmap[A, B](o, f1, f2) 
} 

Ora è possibile regolare la classe caso come questo

case class MyType(tpe: String) 

object MyType { 
    implicit val myTypeFormat = 
    ((__ \ "type").format[String]) build (MyType.apply _, unlift(MyType.unapply)) 
} 

Quindi è possibile utilizzare in questo modo

val myTypeJson = Json.toJson(MyType("bar")) 

println(myTypeJson) 
println(Json.fromJson[MyType](myTypeJson)) 
+0

La soluzione fornita da EECOLOR si riduce all'utilizzo di 'inmap', che trasforma un 'Formato [A]' in un 'Formato [B]'. È tersamente documentato [qui] (http://www.playframework.com/documentation/2.2.x/ScalaJsonCombinators). Puoi anche usare direttamente 'inmap', ma la soluzione di EECOLOR estende in modo pulito i combinatori JSON. – rakensi

0

perché non aggiungere semplicemente un campo non utilizzato alla classe case. metti un commento decente o usa un nome di campo che si spiega da sé.

//f2 is unused field for de/serialization convenience due to limitation in play 

case class SingleField(f1: String, f2: Option[String]) 
object SingleField { 
    implicit val readSingleField : Reads[SingleField] = (
     (__ \ "f1").read[String] and 
      (__ \ "f2").readNullable[String])(SingleField.apply _) 
} 
Problemi correlati