2013-02-23 25 views
14

我試圖序列化/反序列化一些案例類到/從Json ...和我遇到麻煩時處理案例類只有一個字段(我使用的播放2.1):如何序列化/反序列化案例類到Json播放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 _) 
} 

就永遠的代碼生成以下錯誤消息:

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

我知道...包含只是一個字符串沒有多大意義的情況下,類...但我需要序列化/反序列化與上面描述的非常相似的案例類,它來自外部l圖書館。

有什麼想法?我錯過了什麼嗎?任何幫助將非常感激......我越來越瘋狂:-(感謝。

+1

請參閱http://stackoverflow.com/a/20130414/1435971例如,以JSON轉換爲Scala的case類 – prarthan 2013-11-21 20:42:34

回答

23

Json的組合程序不會爲單個字段級的情況下在播放2.1工作(它應該在2.2是可能的)

帕斯卡爾(這個API的作家)解釋這裏這種情況https://groups.google.com/forum/?fromgroups=#!starred/play-framework/hGrveOkbJ6U

有一些解決方法,其工作的,像這樣的:

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

PS:type是Scala一個關鍵字,它不能被用來作爲參數名稱(但我是一個ssume它只是這個例子)

編輯:這種解決方法是不需要與播放2.3.X.宏工作正常。

+2

您可以通過在反引號包裹的字段名稱捕捉'在JSON序列化案例類type' 。我不知道如何告訴你,因爲反引號是代碼塊:) – 2013-02-23 17:11:22

+0

這應該工作:'\'鍵入\'' - 只是用反斜槓,像這樣:\\ \ \ 'type \\'\'' – 2015-01-23 11:01:26

+3

我使用的是Play 2.3(Scala),我也需要工作來支持這個限制。我不認爲它是固定的。 – 2015-02-19 16:17:50

4

問題是(據我所知),Play 2.1框架只能處理從Tuple2開始的元組。在這些例子中它的使用是這樣的:

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

然後用它

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

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

在你的情況,你不能使用and方法(你只有一個值),從而獲得進不去FunctionalBuilder#CanBuildX(其中X是1到22)的那個不錯的apply函數。

爲了提供類似的,你可以創建一個提供build方法具有相似的簽名,那該多好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) 
} 

現在你可以調整你的情況類這樣

case class MyType(tpe: String) 

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

然後你可以使用它像這樣

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

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

EECOLOR給出的解決方案歸結爲使用'inmap',它將'Format [A]'轉換爲'Format [B]'。這是詳細記錄[這裏](http://www.playframework.com/documentation/2.2.x/ScalaJsonCombinators)。您也可以直接使用'inmap',但EECOLOR的解決方案乾淨地擴展了JSON組合器。 – rakensi 2014-01-06 09:11:39

0

爲什麼不簡單地將一個未使用的字段添加到案例類。提出一個體面的評論或使用不言自明的字段名稱。

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