将列表[JsResult [A]]排序到JsResult [List [A]]

时间:2016-02-10 01:17:11

标签: json scala playframework playframework-2.0 play-json

我正在尝试为条带创建一个API,它涉及从Json到case类的大量映射(反之亦然)。我遇到了一个问题,我最终得到了List[JsResult[A]](这是通过映射JObject列表并对它们进行一些操作以将它们映射到适当的case类的结果)。有问题的代码在下面

case class Sources(data: List[PaymentSource],
                     hasMore: Boolean,
                     totalCount: Double,
                     url: String)

  implicit val sourcesReader: Reads[Sources] = {

    val dataAsList = (__ \ "data").read[List[JsObject]].flatMap{jsObjects =>
      val `jsResults` = jsObjects.map{jsObject =>
        val `type` = jsObject \ "type"

        val paymentSource: JsResult[PaymentSource] = `type` match {
          case JsString("card") =>
            Json.fromJson[Card](jsObject)
          case JsString("bitcoin_receiver") =>
            Json.fromJson[BitcoinReceiver](jsObject)
          case JsString(s) =>
            throw UnknownPaymentSource(s)
          case _ =>
            throw new IllegalArgumentException("Expected a Json Object")
        }

        paymentSource
      }

      jsResults

    }

jsResults的类型为List[JsResult[A]],但要正确撰写,我们需要返回JsResult[A]JsError

虽然可以Json.fromJson[Card](jsObject).get代替Json.fromJson[Card](jsObject),但这样做意味着我们失去了Play Json中的累积错误处理(这也意味着我们将错误推送到运行时)

3 个答案:

答案 0 :(得分:3)

您可以使用Reads.list()

val paymentSourceReader: Reads[PaymentSource] = __.read[JsObject].flatMap { o =>
  (__ \ "type").read[String].collect(ValidationError("UnknownPaymentSource")) {
    case "card" =>
      o.as[Card]
    case "bitcoin_receiver" =>
      o.as[BitcoinReceiver]
  }
}
    如果没有read[String] pproperty,则
  1. type创建错误。
  2. collect(ValidationError("UnknownPaymentSource")如果创建错误 键入!(卡| bitcoin_receiver)。
  3. 如果无法投射
  4. o.as[...]抛出异常
  5. 然后使用`paymentSourceReader'

    val dataReader: Reads[List[PaymentSource]] = (__ \ "data").read[List[PaymentSource]](Reads.list(paymentSourceReader))
    

    dataReader可用于复杂的阅读器Reads[PaymentSource],其Sourcesjson.reads(dataReader) JsResult[List[PaymentSource]]的组合器可用于Mekka

答案 1 :(得分:2)

因此,您无法将List[JsResult[A]]转换为JsResult[A],因为如果您有多个成功结果怎么办?这意味着您有A的多个值。您可以将其转换为JsResult[List[A]],有几种方法可以做到这一点,我可能会这样做:

val allErrors = jsResults.collect {
  case JsError(errors) => errors
}.flatten

val jsResult = if (allErrors.nonEmpty) {
  JsError(allErrors)
} else {
  JsSuccess(jsResults.collect {
    case JsSuccess(a, _) => a
  })
}

答案 2 :(得分:0)

我有类似的问题。我想在JsSuccess上加入两个JsResults。 tupledand帮助我完成了这项任务。两者都是play.api.libs.functional包的一部分。

这是你可以将两个JsResult加入一个:

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

((__ \ 'id).validate[Long] and (__ \ 'name).validate[String]).tupled 
    match {
      case JsSuccess((id,name),_) => ...
      case err: JsError =>