如何编写 play-json Reads for Map[(Int, Int), A]?

How to write a play-json Reads for Map[(Int, Int), A]?

我想反序列化这个 json,它代表一个机架,它是一个 5x5 的比赛场地,在位置 (1,2) 包含一个瓷砖:

{
  "rows" : 5,
  "cols" : 5,
  "tiles" : [ {
    "row" : 1,
    "col" : 2,
    "tile" : {
      "number" : 3
    }
  } ]
}

所以我的情况 class 是:

case class Tile(number: Int)
case class Rack(rows: Int, cols: Int, tiles: Map[(Int, Int), Tile])

我试图为 class 机架写一个 Reads:

implicit val tileWrites = Json.writes[Tile]
implicit val tileReads = Json.reads[Tile]

implicit val reads: Reads[Rack] = (
    (__ \ "rows").read[Int] and
      (__ \ "cols").read[Int] and
      (__ \ "tiles").read[Map[(Int, Int), Tile]]
  ) (Rack.apply _)

但是我得到了这个错误:

Error:(50, 26) No Json deserializer found for type Map[(Int, Int),de.htwg.se.rummi.model.model.Tile]. Try to implement an implicit Reads or Format for this type.
      (__ \ "tiles").read[Map[(Int, Int), Tile]]

谁能解释一下我是如何为 Map[(Int, Int), Tile] 编写 Reads 的?

我不建议为 Map 等常见类型定义自定义 Reads 实例。相反,我建议为图块定义自定义类型并为此实现 Reads。优点是您可以将 Reads 实例放在伴随对象中,编译器将始终找到正确的实例,而无需导入任何内容。

case class Tiles(tiles: Map[(Int, Int), Tile])

现在您可以在伴随对象中为 Tiles 定义一个 Reads 实例:

import play.api.libs.json._
import play.api.libs.functional.syntax._

object Tiles {
  implicit val reads: Reads[Tiles] =
    Reads.list {
      (
        (__ \ "row").read[Int] and
        (__ \ "col").read[Int] and
        (__ \ "tile").read[Tile]
      ) { (row, col, tile) => ((row, col), tile) }
    }.map(tuples => Tiles(tuples.toMap))
}