2013-02-23 10 views
14

Ich versuche, einige Fallklassen zu serialisiert/deserialisiert zu/von Json ... und ich habe Probleme, wenn sie mit Fallklassen mit nur einem Feld zu tun (ich verwende 2.1 Play):Wie serialisieren/deserialisieren Fallklassen zu/von 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 _) 
} 

Der obige Code erzeugt immer folgende Fehlermeldung:

[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]      ^

ich weiß ... ein Fall-Klasse, die nur eine Zeichenfolge enthält nicht viel Sinn machen ... aber ich muss eine Fallklasse serialisieren/deserialisieren, die der oben beschriebenen sehr ähnlich ist, die von einem externa kommt l Bibliothek.

Irgendeine Idee? Fehle ich etwas? Jede mögliche Hilfe würde sehr geschätzt werden ... Ich bin immer verrückt :-(Danke.

+1

siehe http://stackoverflow.com/a/20130414/1435971 zum Beispiel json zu scala Fallklasse konvertieren – prarthan

Antwort

23

Json combinators nicht für Einfeldfall-Klasse in Play 2.1 funktioniert

Pascal (es sollte in 2.2 möglich sein) (Schreiber dieses API) hat erklärt, diese Situation hier https://groups.google.com/forum/?fromgroups=#!starred/play-framework/hGrveOkbJ6U

es gibt einige Workarounds, die funktioniert, wie diese:

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

ps: type ist ein Schlüsselwort in Scala, es kann nicht verwendet werden, wie Parametername (aber ich a ssume es nur für dieses Beispiel ist)

edit: Diese Problemumgehung ist noch nicht mit einem Spiel 2.3.x erforderlich Das Makro funktioniert gut.

+2

Sie 'in einer JSON Baumuster zur Serialisierung Fall Klasse erfassen können in Backticks die Feldnamen durch Umwickeln .Ich kann nicht herausfinden, wie man das in Markdown zeigt, weil die Backticks die Codeblöcke sind :) –

+0

sollte das funktionieren: '\' type \ '' - entkomme einfach mit einem umgekehrten Schrägstrich, wie folgt: '\' \\ 'type \\' \ '' –

+3

Ich benutze Play 2.3 (Scala) und ich musste auch diese Einschränkung bearbeiten. Ich glaube nicht, dass es schon repariert ist. –

4

Problem ist, dass (soweit ich das beurteilen kann) das Play 2.1-Framework nur Tupel ab Tuple2 behandelt. In den Beispielen es wie folgt verwendet wird:

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

Und dann ist es zu

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

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

In Ihrem Fall verwenden Sie die and Methode verwenden (Sie haben nur einen Wert) und erhalten somit keinen Zugang zu diese nette apply Funktion von FunctionalBuilder#CanBuildX (wobei X 1 bis 22 ist).

Um etwas ähnliches Sie liefern kann eine implizite Klasse erstellen, wie die schöne apply Methode

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

Jetzt ein build Verfahren mit einer ähnlichen Signatur bereitstellt, können Sie Ihren Fall Klasse wie folgt

case class MyType(tpe: String) 

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

Dann können Sie es wie nutzen diese

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

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

Die Lösung von EECOLOR läuft auf die Verwendung von 'inmap' hinaus, die ein 'Format [A]' in ein 'Format [B]' umwandelt. Es ist knapp dokumentiert [http://www.playframework.com/documentation/2.2.x/ScalaJsonCombinators]. Sie können auch 'inmap' direkt verwenden, aber die Lösung von EECOLOR erweitert die JSON-Kombinatoren sauber. – rakensi

0

warum nicht einfach ein unbenutztes Feld zur Fallklasse hinzufügen. Setzen Sie einen anständigen Kommentar ein oder verwenden Sie einen Feldnamen, der selbsterklärend ist.

//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 _) 
} 
Verwandte Themen