您將無法在這裏使用JSON播放組合程序的一切,因爲它們只與固定字段映射工作。爲了能夠閱讀elements
字段,您需要實施Reads[List[(String, String)]]
。幸運的是,Play已經有Reads[Map[A, B]]
可用(對於類型A
和B
也有Reads
),並且Map[A, B]
可以很容易地轉換爲List[(A, B)]
(在Map
下面只是元組的集合)。
對於一次性案例,我們可以使用read[Map[String, String]]
和map
將其轉換爲List
。然後,我們可以將它映射到案例類。假設下面的JSON結構:
val js = Json.parse("""{"element": { "foo": "bar", "barfoo": "foobar"}}""")
你可以寫:
implicit val reads = (__ \ "elements").read[Map[String, String]]
.map(_.toList)
.map(tuples => Model(tuples))
,並嘗試一下:
scala> js.validate[Model]
res8: play.api.libs.json.JsResult[Model] = JsSuccess(Model(List((foo,bar), (barfoo,foobar))),/elements)
請注意以上Reads[Model]
是怎樣的一個特例,因爲案例類只有一個字段。藉此遠一點,看看它是如何與JSON組合子玩,讓我們添加一個新的領域:
case class Model(elements: List[(String, String)], info: String)
然後,我們也使我們的Reads
的元組多一些通用的,所以它可以處理值任何類型的A
其中Reads[A]
可用的:
implicit def tupleReads[A](implicit rds: Reads[A]): Reads[List[(String, A)]] =
Reads.mapReads(rds).map(_.toList)
現在我們可以寫一個Reads
使用組合子的新定義Model
,一樣的,你已經習慣了:
implicit val reads = (
(__ \ "elements").read[List[(String, String)]] and
(__ \ "info").read[String]
)(Model.apply _)
想出來:
val js = Json.parse("""{"elements": { "foo": "bar", "barfoo": "foobar"}, "info": "test"}""")
scala> js.validate[Model]
res0: play.api.libs.json.JsResult[Model] = JsSuccess(Model(List((foo,bar), (barfoo,foobar)),test),)
如果你的JSON結構只有看起來像{"foo": "bar", "barfoo": "foobar"}
(無elements
鍵),那麼我們仍然可以利用相同的通用Reads[List[(String, A)]]
,但將需要執行更多的自定義Reads[Model]
將整個對象映射到一個模型字段。讓我們想映射上述JSON到:
Model(List(("foo" -> "bar"), ("barfoo" -> "foobar")))
我們需要將基本上是相同的,因爲我定義的第一個,但我們可以從它刪除JsPath
的Reads[Model]
:
// Use `tupleReads` as defined above, restricted to `String`
implicit val reads = tupleReads[String].map(tuples => Model(tuples))
它的工作原理如下:
val js = Json.parse("""{"foo": "bar", "barfoo": "foobar"}""")
scala> js.validate[Model]
res0: play.api.libs.json.JsResult[Model] = JsSuccess(Model(List((foo,bar), (barfoo,foobar))),)
夢幻般的答案。非常感謝邁克爾! –