2014-12-04 37 views
6

我正在嘗試使用play-reactivemongo和reactivemongo-extensions將一個Rails/Mongodb應用程序遷移到Play 2.3。在建模數據時,我正在運行一個序列化和反序列化Map [Int,Boolean]的問題。播放JSON格式的地圖[詮釋,_]

當我嘗試通過宏來定義我的格式,像這樣

implicit val myCaseClass = Json.format[MyCaseClass] 

其中MyCaseClass有幾個字符串字段,一個BSONObjectID字段和地圖[詮釋,布爾]字段中的編譯器抱怨:

No Json serializer found for type Map[Int,Boolean]. Try to implement an implicit Writes or Format for this type. 
No Json deserializer found for type Map[Int,Boolean]. Try to implement an implicit Reads or Format for this type. 

查看Reads.scala中Play的源代碼,我看到爲Map [String,_]定義的Reads,但Map [Int,_]沒有定義。

Play有默認的讀/寫字符串映射但沒有其他簡單類型的原因嗎?

我不完全理解由play對象定義的Map [String,_],因爲我對scala相當陌生。我將如何將其轉換爲Map [Int,_]?如果由於某些技術原因,這是不可能的,我將如何定義一個Reads/Writes for Map [Int,Boolean]?

回答

12

你可以編寫自己的讀寫操作。

在你的情況下,這應該是這樣的:

implicit val mapReads: Reads[Map[Int, Boolean]] = new Reads[Map[Int, Boolean]] { 
    def reads(jv: JsValue): JsResult[Map[Int, Boolean]] = 
     JsSuccess(jv.as[Map[String, Boolean]].map{case (k, v) => 
      Integer.parseInt(k) -> v .asInstanceOf[Boolean] 
     }) 
} 

implicit val mapWrites: Writes[Map[Int, Boolean]] = new Writes[Map[Int, Boolean]] { 
    def writes(map: Map[Int, Boolean]): JsValue = 
     Json.obj(map.map{case (s, o) => 
      val ret: (String, JsValueWrapper) = s.toString -> JsBoolean(o) 
      ret 
     }.toSeq:_*) 
} 

implicit val mapFormat: Format[Map[Int, Boolean]] = Format(mapReads, mapWrites) 

我有打2.3進行了測試。不過,我不確定這是否是在服務器端使用Map [Int,Boolean]和在客戶端使用字符串 - >布爾映射的json對象的最佳方法。

6

JSON只允許字符串鍵(它從JavaScript繼承的限制)。

+0

如果我停下來思考片刻,我會意識到我已經知道那個哈哈。就在我變得密集的時候,對不起。無論如何,是否可以定義Reads/Writes,使scala對象具有Map [Int,Boolean],但用字符串鍵寫入JSON對象?基本上解析來自所有JSON鍵的整數來創建一個Map [Int,_]? – imagio 2014-12-04 04:15:34

+0

我建議你回去改進你的問題。然後希望其他人(誰更瞭解比賽)會回答。 – 2014-12-04 04:35:55

1

感謝塞斯蒂瑟。這是我的「泛型」(一半)。

「一半」,因爲它不處理通用密鑰。一個可以複製粘貼,並與「內部」代替「龍」

「摘要」是一種類型,我想序列化(它需要自己的 串行)

/** this is how to create reader and writer or format for Maps*/ 
// implicit val mapReads: Reads[Map[Long, Summary]] = new MapLongReads[Summary] 
// implicit val mapWrites: Writes[Map[Long, Summary]] = new MapLongWrites[Summary] 
implicit val mapLongSummaryFormat: Format[Map[Long, Summary]] = new MapLongFormats[Summary] 

這是必需的執行:

class MapLongReads[T]()(implicit reads: Reads[T]) extends Reads[Map[Long, T]] { 
    def reads(jv: JsValue): JsResult[Map[Long, T]] = 
    JsSuccess(jv.as[Map[String, T]].map{case (k, v) => 
     k.toString.toLong -> v .asInstanceOf[T] 
    }) 
} 

class MapLongWrites[T]()(implicit writes: Writes[T]) extends Writes[Map[Long, T]] { 
    def writes(map: Map[Long, T]): JsValue = 
    Json.obj(map.map{case (s, o) => 
     val ret: (String, JsValueWrapper) = s.toString -> Json.toJson(o) 
     ret 
    }.toSeq:_*) 
} 

class MapLongFormats[T]()(implicit format: Format[T]) extends Format[Map[Long, T]]{ 
    override def reads(json: JsValue): JsResult[Map[Long, T]] = new MapLongReads[T].reads(json) 
    override def writes(o: Map[Long, T]): JsValue = new MapLongWrites[T].writes(o) 
} 
0

我們可以3x14159265和Seth Tisue感謝的解決方案推廣到2小類型類:

import play.api.libs.json.Json.JsValueWrapper 
import play.api.libs.json._ 
import simulacrum._ 

object MapFormat { 

    @typeclass trait ToString[A] { 
    def toStringValue(v: A): String 
    } 
    @typeclass trait FromString[A] { 
    def fromString(v: String): A 
    } 

    implicit def mapReads[K, V: Reads](implicit fstc: FromString[K]): Reads[Map[K, V]] = new Reads[Map[K, V]] { 
    def reads(jv: JsValue): JsResult[Map[K, V]] = 
     JsSuccess(jv.as[Map[String, V]].map { case (k, v) => fstc.fromString(k) -> v }) 
    } 

    implicit def mapWrites[K, V: Writes](implicit tstc: ToString[K]): Writes[Map[K, V]] = new Writes[Map[K, V]] { 
    def writes(map: Map[K, V]): JsValue = 
     Json.obj(map.map { 
     case (s, o) => 
      val ret: (String, JsValueWrapper) = tstc.toStringValue(s) -> o 
      ret 
     }.toSeq: _*) 
    } 

    implicit def mapFormat[K, V: Format](implicit tstc: ToString[K], fstc: FromString[K]): Format[Map[K, V]] = 
    Format(mapReads, mapWrites) 

} 

請注意,我使用Simulacrum(https://github.com/mpilquist/simulacrum)來定義我的類型類。

下面是如何使用它的一個例子:

case class UserId(value: String) 
object UserId { 
    implicit val userIdToString: ToString[UserId] = new ToString[UserId] { 
    def toStringValue(v: A): String = v.value 
    } 
    implicit val userIdToString: FromString[UserId] = new FromString[UserId] { 
    def fromString(v: String): A = UserId(v) 
    } 
} 

object MyApp extends App { 

    import MapFormat._ 

    val myMap: Map[UserId, Something] = Map(...) 

    Json.toJson(myMap) 
} 

如果說的IntelliJ您import MapFormat._從未使用過,你可以和這樣的:implicitly[Format[Map[UserId, Something]]]只是進口下方。它會修復pb。 ;)