これはかなり一般的な質問です。この回答はReads.traversableReads[F[_], A]
に触発されています。
Reads[A]
という累積の考え方をサポートするには、あなたのすべてのGenererated Reads[JsObject]
を試してみて、[エラー、Vector [JsObject]]のいずれかを使用します。元の 'Reads.traversableReads [F [_]、A]'はReads[List[A]]
または何らかのコレクションを返しますが、単純なJsonが必要です。問題ありません。++
はJsObjects
をconcatinatesします。
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]
ですので、Seq(Reads[JsObject])
をFunctionalBuilder
(and
、次にreduce
)に減らすことができます。
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]
マッピングをベースにしていますが、最後の一般的Seq(Reads[JsObject]) => Reads[JsObject]
基本的なJSONコンビネータの原則に基づいて、問題が解決しますが、タスク自体は正しくないです。読み込みを制御しない場合は、同じパスが2回使用されるかどうかを確認します。
私はそれを完全には理解していませんが、間違いなく私が望んだことは間違いありません。確かに、コードを使って遊ぶほど、私はそれを理解します。ありがとう! – Nick