Play Framework:如何在验证时将字符串转换为数字 JSON

Play Framework: How to convert strings to numbers while validating JSON

给出以下 JSON..

{
    "ask":"428.00",
    "bid":"424.20"
}

...我需要将 askbid 的值转换为数字:

{
    "ask": 428.00,
    "bid": 424.20
}

为此,我创建了一个验证器来读取字符串值并将其传递给方法 toNumber,该方法验证并转换给定的字符串:

def validate = (
  ((__ \ 'ask).json.pickBranch(Reads.of[JsString] <~ toNumber)) ~
  ((__ \ 'bid).json.pickBranch(Reads.of[JsString] <~ toNumber))
).reduce

private def toNumber(implicit reads: Reads[String]) = {
  Reads[Double](js =>
    reads.reads(js).flatMap { value =>
      parse[Double](value) match {
        case Some(number) => JsSuccess(number)
        case _ => JsError(ValidationError("error.number", value))
      }
    }
  )
}

上面的代码只验证了值,当然没有用转换后的数字替换原始字符串。如何在验证时将字符串值转换为数字?

编辑

只想分享Ben提供的解决方案:

def validate = (
  ((__ \ 'ask).json.update(toNumber)) ~
  ((__ \ 'bid).json.update(toNumber))
).reduce

private def toNumber(implicit reads: Reads[String]) = {
  Reads[JsNumber](js =>
    reads.reads(js).flatMap { value =>
      parse[Double](value) match {
        case Some(number) => JsSuccess(JsNumber(number))
        case _ => JsError(ValidationError("error.number", value))
      }
    }
  )
}

如果您将 toNumber 设为 Reads[JsNumber] 而不是 Reads[Double](只需将 number 包裹在 JsNumber 中),那么您可以使用 transform 连同 update:

val transformer = (__ \ "ask").json.update(toNumber)
val json = Json.parse(""" { "ask" : "44" } """)
json.transorm(transformer) //JsSuccess({"ask":44.0},/ask)

val json = Json.parse(""" { "ask" : "foo" } """)
json.transorm(transformer) //JsError(List((/ask,List(ValidationError(error.number,WrappedArray(foo))))))

从某种意义上说,变形金刚 验证器。您可以使用 transform 简单地转换值,如果转换无效则得到 JsError,而不是检查某物是否有效,然后转换它。阅读更多关于 transform here.