如何为给定的Reads [T]写一个Reads [Seq [T]]?

时间:2014-06-12 18:38:31

标签: json scala playframework

我正在使用play框架版本2.2.3的json库。我有以下json对象:

{
    "myData":
    [
        {
            "A": "some text",
            "B": [10, 20, 30]
        },
        {
            "A": "some other text",
            "B": [15, 25, 35]
        },
        ...
    ]
}

我想将此json对象反序列化为Vector[Map[String, Vector[Int]]]。所以结果应该是:

Vector(Map("some text" -> Vector(10, 20, 30)), Map("some other text" -> Vector(15, 25, 35)))

在我尝试实现这一目标的过程中,我能够编写一个Reads[Map[String, Vector[Int]]]来为单个条目执行此操作。

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

implicit val singleEntryReads: Reads[Map[String, Vector[Int]]] = {
  (__).read(
    (__ \ "A").read[String] and
    (__ \ "B").read[Vector[Int]] tupled) map { keyAndValue =>
      val (a, b) = keyAndValue
      Map(a -> b)
    }
}

因此转换适用于单个条目:

scala> (myJsonObject \ "myData")(0).validate[Map[String, Vector[Int]]]     
res: play.api.libs.json.JsResult[Map[String, Vector[Int]]] = JsSuccess(Map(some text -> Vector(10, 20, 30)))

但我怎么能写一个Reads[Vector[Map[String, Vector[Int]]]]?我最好的想法是将其与this older question给出的答案类似:

implicit val allEntriesReads: Reads[Seq[Map[String, Vector[Int]]]] = Reads.seq(singleEntryReads)

我试着像这样使用它:

scala> (myJsonObject \ "myData").validate[Seq[Map[String, Vector[Int]]]]"
res2: play.api.libs.json.JsResult[Seq[Map[String, Vector[Int]]]] = JsError(List(((147)/B,List(ValidationError(error.path.missing,WrappedArray()))), ((148)/B,List(ValidationError(error.path.missing,WrappedArray())))))

但这不起作用并给我一个JsError()。我如何实现第二个Reads以使其以这种方式工作?

1 个答案:

答案 0 :(得分:2)

你不需要。玩!附带一个隐含的(在Reads伴随对象中定义)已经做了你想做的事情:

implicit def traversableReads[F[_], A](implicit bf: CanBuildFrom[F[_], A, F[A]], ra: Reads[A]): Reads[F[A]]

如果序列类型有隐式Reads[A]和适当的CanBuildFrom(标准库集合类型已经存在,例如Vector),那么这个隐式将充当隐式Reads[F[A]]其中F是集合类型。

Scala允许您定义一个本身采用隐式参数的implicit def,它将作为其返回类型的隐式值。使用implicit def时,Scala将在呼叫站点搜索隐式参数。所以:

.validate[Seq[Map[String, Vector[Int]]]]

变为:

.validate[Seq[Map[String, Vector[Int]]]](traversableReads)

然后成为:

.validate[Seq[Map[String, Vector[Int]]]](traversableReads(singleEntryReads, Seq.canBuildFrom)