這是一個相當普遍的問題。這個答案受Reads.traversableReads[F[_], A]
的啓發。
爲了支持累積Reads[A]
的想法,我們必須嘗試所有的生成Reads[JsObject]
,我們將使用Either [Errors,Vector [JsObject]]。在原來的'Reads.traversableReads [F [_],A]'返回Reads[List[A]]
或某些集合,但我們需要簡單的Json,沒問題,++
concizaates我們的JsObjects
。
def reduceReads(generated: Seq[Reads[JsObject]]) = Reads {json =>
type Errors = Seq[(JsPath, Seq[ValidationError])]
def locate(e: Errors, idx: Int) = e.map { case (p, valerr) => (JsPath(idx)) ++ p -> valerr }
generated.iterator.zipWithIndex.foldLeft(Right(Vector.empty): Either[Errors, Vector[JsObject]]) {
case (acc, (r, idx)) => (acc, r.reads(json)) match {
case (Right(vs), JsSuccess(v, _)) => Right(vs :+ v)
case (Right(_), JsError(e)) => Left(locate(e, idx))
case (Left(e), _: JsSuccess[_]) => Left(e)
case (Left(e1), JsError(e2)) => Left(e1 ++ locate(e2, idx))
}
}
.fold(JsError.apply, { res =>
JsSuccess(res.fold(Json.obj())(_ ++ _))
})
}
scala> json: play.api.libs.json.JsValue = {"attr1":{"attr1a":"attr1a"},"attr2":"attr2"}
scala> res7: play.api.libs.json.JsResult[play.api.libs.json.JsObject] = JsSuccess({"attr1":"attr1a","attr2":"attr2"},)
新真棒簡單的答案
之後幾天,我有這個絕妙的主意。 object Reads
隱含Reducer[JsObject, JsObject]
,所以我們可以用FunctionalBuilder
(and
然後reduce
)減少Seq(Reads[JsObject])
!
def reduceReads(generated: Seq[Reads[JsObject]]) =
generated.foldLeft(Reads.pure(Json.obj())){
case (acc, r) =>
(acc and r).reduce
}
該解決方案簡單明瞭。最初的想法基於Seq(Reads[JsObject]) => Seq(JsResult[JsObject]) => Reads[JsObject]
映射,但基於基本的Json最後一個組合子原則Seq(Reads[JsObject]) => Reads[JsObject]
一般來說,問題就解決了,但任務本身是不正確的。如果你不控制閱讀,你想看看如果相同的路徑將被使用兩次?
我必須承認,現在我不完全理解它,但它絕對做我想要的。我敢肯定,我玩的代碼越多,我就知道了。謝謝! – Nick