2013-02-23 8 views
14

Próbuję serializować/deserializować niektóre klasy przypadków do/z Json ... i mam kłopoty, gdy mamy do czynienia z klasami przypadków z jednym polem (Używam Zagraj 2.1):Jak serializować/deserializować klasy przypadków do/z Json w grze 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 _) 
} 

powyższy kod zawsze generuje następujący komunikat o błędzie:

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

wiem ... klasę przypadek, który zawiera tylko ciąg nie ma sensu ... ale muszę serializować/deserializować klasę przypadków bardzo podobną do tej, którą opisałem powyżej, która pochodzi z externy l biblioteka.

Każdy pomysł? Czy czegoś brakuje? Każda pomoc będzie bardzo mile widziane ... Dostaję szalony :-(Dzięki.

+1

zapoznaj http://stackoverflow.com/a/20130414/1435971 na przykład przekonwertować json Scala klasie przypadku – prarthan

Odpowiedz

23

JSON kombinatorów nie działa dla jednej klasy w przypadku pola zagraj 2.1 (powinno być możliwe w 2.2)

Pascal (autor tego API) wyjaśnił tę sytuację tutaj https://groups.google.com/forum/?fromgroups=#!starred/play-framework/hGrveOkbJ6U

Istnieją pewne obejścia, które działa, jak to jedno:

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

ps: type jest kluczowe w Scala, nie może być stosowany jako nazwa parametru (ale ja ssume to tylko dla tego przykładu)

edit: To obejście nie jest jeszcze wymagane w przypadku gry 2.3.X. Makro działa dobrze.

+2

Można uchwycić 'type' w JSON serializacji klasy case owijając nazwę pola w backticks .Nie mogę wymyślić, jak pokazać to w przecenach, ponieważ backticks są blokami kodu :) –

+0

to powinno działać: '\' type \ '' - po prostu uciec z nimi z ukośnikiem odwrotnym, tak: '\' \\ 'type \\' \ '' –

+3

Używam Play 2.3 (Scala) i musiałem również pracować nad tym ograniczeniem. Nie sądzę, żeby to było jeszcze naprawione. –

4

Problem polega na tym, że (o ile wiem) struktura Play 2.1 obsługuje tylko krotki, począwszy od Tuple2. W przykładach jest używany tak:

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

A następnie używać go

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

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

w Twoim przypadku nie można użyć metody and (masz tylko jedną wartość), a tym samym uzyskać dostępu do ta miła funkcja apply z FunctionalBuilder#CanBuildX (gdzie X to 1 do 22).

W celu dostarczenia coś podobnego można utworzyć niejawny Klasa udostępniająca metodę build z podobnym podpisem jako miłe apply metody

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

Teraz można dostosować klasę takim wypadku

case class MyType(tpe: String) 

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

Wtedy można go używać tak

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

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

Rozwiązanie podane przez EECOLOR sprowadza się do użycia "inmap", które przekształca "Format [A]" na "Format [B]". Jest to krótko udokumentowane [tutaj] (http://www.playframework.com/documentation/2.2.x/ScalaJsonCombinators). Możesz również użyć 'inmap' bezpośrednio, ale rozwiązanie EECOLOR czysto rozszerza kombinatory JSON. – rakensi

0

dlaczego nie po prostu dodać nieużywane pole do klasy sprawy. wstawić przyzwoity komentarz lub użyć nazwy pola, która jest oczywista.

//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 _) 
} 
Powiązane problemy