首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Play JSON:将Seq[ Reads[JsObject] ]转换为一个Reads[JsObject]

Play JSON:将Seq[ Reads[JsObject] ]转换为一个Reads[JsObject]
EN

Stack Overflow用户
提问于 2016-04-25 19:05:01
回答 1查看 492关注 0票数 4

我动态地生成一堆Reads[JsObject],然后在Seq[Reads[JsObject]]中使用。为了实际应用所有这些单一Reads[JsObject],我必须将它们与and合并成一个Reads[JsObject]。这个是可能的吗?

我有(例子):

代码语言:javascript
复制
val generatedReads: Seq[Reads[JsObject]] = Seq(
    (__ \ "attr1").json.copyFrom((__ \ "attr1" \ "attr1a").json.pick),
    (__ \ "attr2").json.pickBranch
)

我需要的是:

代码语言:javascript
复制
val finalReads: Reads[JsObject] =
    (__ \ "attr1").json.copyFrom((__ \ "attr1" \ "attr1a").json.pick) and
    (__ \ "attr2").json.pickBranch

在编译时不知道属性名称和要选择哪个分支,这就是为什么它必须是动态的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-04-26 04:59:44

这是一个相当常见的问题。这个答案受到Reads.traversableReads[F[_], A]的启发。

为了支持Reads[A]累积的思想,我们必须尝试所有生成的Reads[JsObject],并为此使用[Error,VectorJsObject]。在原始的'Reads.traversableReads[F_,A]‘中,返回Reads[List[A]]或一些集合,但是我们需要简单的Json,没有问题,++连接我们的JsObjects

代码语言:javascript
复制
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来减少Seq(Reads[JsObject]) (and,然后是reduce)!

代码语言:javascript
复制
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]

一般来说,问题已经解决了,但任务本身是不正确的。如果您不控制读取,那么您希望看到同一路径是否会被使用两次吗?

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36849032

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档