Scala Play 2.6:使用字段之间的约束解析json

时间:2018-05-23 19:14:54

标签: json scala playframework

我希望在解析json时根据另一个字段的值验证字段。

例如,当我读取范围时,我想验证min<最大:

import org.scalatest.{FlatSpec, Matchers}
import play.api.libs.json.{JsError, Json, Reads}

class JsonReadsTest extends FlatSpec with Matchers {

  "Json" should "be reads" in {
    val reads: Reads[Range] = ???
    val json = Json.obj("min" -> 3, "max" -> 2)
    reads.reads(json) shouldBe JsError("max should be superior to min")
  }
}

case class Range(min: Int, max: Int)

1 个答案:

答案 0 :(得分:1)

Stright-forward-oop解决方案

您可以创建一个扩展Reads [T]的对象,并直接实现read方法,而无需使用功能构建器 (这种方法在文档中没有完全涵盖,但你可以找到很多考试in source code

val reads1 = new Reads[Range] {
  def reads(json: JsValue): JsResult[Range] = {
    (for {
      min <- (json \ "min").validate[Int]
      max <- (json \ "max").validate[Int]
    } yield (min, max)).flatMap {
      case (min, max) if max > min =>
        JsSuccess(Range(min, max))
      case _ =>
        JsError(Seq(JsPath ->
          Seq(JsonValidationError("error.expected.range"))))
    }
  }
}

功能构建器解决方案

val reads2 = (
  (__ \ "min").read[Int] and
  (__ \ "max").read[Int]
).tupled
.filter(JsonValidationError("error.expected.range")){ case (min, max) => max > min}
.map{ case (min, max) => Range(min, max)}

最佳实践

您正在尝试混合阅读和验证,如果您在不同模块中使用不同的验证规则,则可能会出现问题。有一种做法是将json对象解析为类似

的表示模型类
case class RangeInput(min: Int, max: Int)

然后将其转换为执行验证的业务模型类

def validate(input: RangeInput): Option[Range] = 
  input.filter(i => i.max > i.min).map(i => Range(i.min, i.max))

如果您需要汇总验证错误,cats validated之类的内容可以帮助您解决这个问题