3

我動態生成一串Reads[JsObject],然後我在Seq[Reads[JsObject]]中有一串。爲了實際應用所有這些單個Reads[JsObject],我必須將它們與and合併爲一個Reads[JsObject]。這可能嗎?播放JSON:將一個Seq [將[JsObject]]讀入一個讀取[JsObject]

我有(例如):

val generatedReads: Seq[Reads[JsObject]] = Seq(
    (__ \ "attr1").json.copyFrom((__ \ "attr1" \ "attr1a").json.pick), 
    (__ \ "attr2").json.pickBranch 
) 

我需要什麼:

val finalReads: Reads[JsObject] = 
    (__ \ "attr1").json.copyFrom((__ \ "attr1" \ "attr1a").json.pick) and 
    (__ \ "attr2").json.pickBranch 

屬性名稱和分支來接在編譯的時候是不知道,這就是爲什麼它得充滿活力。

回答

3

這是一個相當普遍的問題。這個答案受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],所以我們可以用FunctionalBuilderand然後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]


一般來說,問題就解決了,但任務本身是不正確的。如果你不控制閱讀,你想看看如果相同的路徑將被使用兩次?

+0

我必須承認,現在我不完全理解它,但它絕對做我想要的。我敢肯定,我玩的代碼越多,我就知道了。謝謝! – Nick