将 List[JsResult[A]] 序列化为 JsResult[List[A]]
Sequence a List[JsResult[A]] to a JsResult[List[A]]
我正在尝试为 stripe 创建一个 API,其中涉及从 Json 到大小写 classes 的大量映射(反之亦然)。我遇到了一个问题,我最终得到了 List[JsResult[A]]
(这是通过 JObject 的列表进行映射并对它们进行一些操作以将它们映射到适当的情况 class 的结果)。有问题的代码如下
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]]
,但是要将其与读取正确组合,我们需要 return JsResult[A]
或 JsError
。
虽然可以 Json.fromJson[Card](jsObject).get
而不是 Json.fromJson[Card](jsObject)
,但这样做意味着我们失去了 Play Json 中的累积错误处理(这也意味着我们将错误推入运行时)
所以,你不能把一个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
})
}
您可以使用 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]
如果没有 type
属性则创建错误。
collect(ValidationError("UnknownPaymentSource")
创建错误如果
输入 !(card|bitcoin_receiver)。
o.as[...]
如果无法转换则抛出异常
然后使用`paymentSourceReader'
val dataReader: Reads[List[PaymentSource]] = (__ \ "data").read[List[PaymentSource]](Reads.list(paymentSourceReader))
dataReader
可以在复杂的 reader Reads[PaymentSource]
中使用 Sources
或 json.reads(dataReader)
的组合子 JsResult[List[PaymentSource]]
我遇到了类似的问题。我想将两个 JsResults 加入到 JsSuccess 中。 tupled
和 and
帮助我完成了这个任务。两者都是 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 =>
我正在尝试为 stripe 创建一个 API,其中涉及从 Json 到大小写 classes 的大量映射(反之亦然)。我遇到了一个问题,我最终得到了 List[JsResult[A]]
(这是通过 JObject 的列表进行映射并对它们进行一些操作以将它们映射到适当的情况 class 的结果)。有问题的代码如下
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]]
,但是要将其与读取正确组合,我们需要 return JsResult[A]
或 JsError
。
虽然可以 Json.fromJson[Card](jsObject).get
而不是 Json.fromJson[Card](jsObject)
,但这样做意味着我们失去了 Play Json 中的累积错误处理(这也意味着我们将错误推入运行时)
所以,你不能把一个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
})
}
您可以使用 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]
如果没有type
属性则创建错误。collect(ValidationError("UnknownPaymentSource")
创建错误如果 输入 !(card|bitcoin_receiver)。o.as[...]
如果无法转换则抛出异常
然后使用`paymentSourceReader'
val dataReader: Reads[List[PaymentSource]] = (__ \ "data").read[List[PaymentSource]](Reads.list(paymentSourceReader))
dataReader
可以在复杂的 reader Reads[PaymentSource]
中使用 Sources
或 json.reads(dataReader)
的组合子 JsResult[List[PaymentSource]]
我遇到了类似的问题。我想将两个 JsResults 加入到 JsSuccess 中。 tupled
和 and
帮助我完成了这个任务。两者都是 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 =>