gpt4 book ai didi

json - Play JSON : Reading and validating a JsObject with unknown keys

转载 作者:行者123 更新时间:2023-12-01 04:56:36 26 4
gpt4 key购买 nike

我正在使用多个 Reads[T] 读取嵌套的 JSON 文档但是,我坚持使用以下子对象:

{
...,
"attributes": {
"keyA": [1.68, 5.47, 3.57],
"KeyB": [true],
"keyC": ["Lorem", "Ipsum"]
},
...
}

键(“keyA”、“keyB”...)以及键的数量在编译时是未知的,并且可能会有所不同。键的值总是 JsArray实例,但大小和类型不同(但是,特定数组的所有元素必须具有相同的 JsValue 类型)。

单个属性的 Scala 表示:
case class Attribute[A](name: String, values: Seq[A])
// 'A' can only be String, Boolean or Double

目标是创建一个 Reads[Seq[Attribute] ] 在转换整个文档时可用于“属性”字段(请记住,“属性”只是一个子文档)。

然后是一个简单的映射,其中包含应用于验证属性的键和数组类型的允许组合。编辑:这张 map 是 特定于每个请求 (或者更确切地说是针对每种类型的 json 文档)。但是您可以假设它在范围内始终可用。
val required = Map(
"KeyA" -> "Double",
"KeyB" -> "String",
"KeyD" -> "String",
)

所以在上面显示的 JSON 的情况下, Reads应该会产生两个错误:
  • “keyB”确实存在,但类型错误(预期的字符串,是 bool 值)。
  • 缺少“keyD”(而不需要 keyC,可以忽略)。


  • 我无法创建必要的 Reads .我作为第一步尝试的第一件事,从外部 Reads 的角度来看:
    ...
    (__ \ "attributes").reads[Map[String, JsArray]]...
    ...

    我认为这是一个不错的第一步,因为如果 JSON 结构不是包含 String 的对象s 和 JsArray s 作为键值对,然后是 Reads失败并显示正确的错误消息。它有效,但是:我不知道如何从那里继续。当然,我可以创建一个方法来转换 MapSeq[Attribute] ,但是这个方法应该返回一个 JsResult ,因为还有进一步的验证要做。

    我尝试的第二件事:
      val attributeSeqReads = new Reads[Seq[Attribute]] {
    def reads(json: JsValue) = json match {
    case JsObject(fields) => processAttributes(fields)
    case _ => JsError("attributes not an object")
    }
    def processAttributes(fields: Map[String, JsValue]): JsResult[Seq[Attribute]] = {
    // ...
    }
    }

    这个想法是在 processAttributes 内手动验证 map 的每个元素.但我觉得这太复杂了。任何帮助表示赞赏。

    编辑澄清:

    在帖子的开头我说 key (keyA,keyB...)在编译时是未知的。后来我说那些键是 map 的一部分 required用于验证。这听起来很矛盾,但事实是: required特定于每个文档/请求,并且在编译时也不知道。但是您不必担心这一点,只需假设对于每个请求都是正确的 required已经在范围内可用。

    最佳答案

    你对任务太困惑了

    The keys ("keyA", "keyB"...) as well as the amount of keys are not known at compile time and can vary



    那么key的数量和类型是事先知道的,最后知道的呢?

    So in the case of the JSON shown above, the Reads should create two errors:

    1. "keyB" does exist, but has the wrong type (expected String, was boolean).

    2. "keyD" is missing (whereas keyC is not needed and can be ignored).



    您的主要任务只是检查可用性和合规性?

    您可以实现 Reads[Attribute]Reads.list(Reads.of[A]) 为您的每把 key (此读取将检查类型和必需)并跳过省略(如果不需要) Reads.pure(Attribute[A]) .然后元组转换为列表( _.productIterator.toList ),你会得到 Seq[Attribute]
    val r = (
    (__ \ "attributes" \ "keyA").read[Attribute[Double]](list(of[Double]).map(Attribute("keyA", _))) and
    (__ \ "attributes" \ "keyB").read[Attribute[Boolean]](list(of[Boolean]).map(Attribute("keyB", _))) and
    ((__ \ "attributes" \ "keyC").read[Attribute[String]](list(of[String]).map(Attribute("keyC", _))) or Reads.pure(Attribute[String]("keyC", List()))) and
    (__ \ "attributes" \ "keyD").read[Attribute[String]](list(of[String]).map(Attribute("keyD", _)))
    ).tupled.map(_.productIterator.toList)

    scala>json1: play.api.libs.json.JsValue = {"attributes":{"keyA":[1.68,5.47,3.57],"keyB":[true],"keyD":["Lorem","Ipsum"]}}

    scala>res37: play.api.libs.json.JsResult[List[Any]] = JsSuccess(List(Attribute(keyA,List(1.68, 5.47, 3.57)), Attribute(KeyB,List(true)), Attribute(keyC,List()), Attribute(KeyD,List(Lorem, Ipsum))),)

    scala>json2: play.api.libs.json.JsValue = {"attributes":{"keyA":[1.68,5.47,3.57],"keyB":[true],"keyC":["Lorem","Ipsum"]}}

    scala>res38: play.api.libs.json.JsResult[List[Any]] = JsError(List((/attributes/keyD,List(ValidationError(List(error.path.missing),WrappedArray())))))

    scala>json3: play.api.libs.json.JsValue = {"attributes":{"keyA":[1.68,5.47,3.57],"keyB":["Lorem"],"keyC":["Lorem","Ipsum"]}}

    scala>res42: play.api.libs.json.JsResult[List[Any]] = JsError(List((/attributes/keyD,List(ValidationError(List(error.path.missing),WrappedArray()))), (/attributes/keyB(0),List(ValidationError(List(error.expected.jsboolean),WrappedArray())))))

    如果您将有超过 22 个属性,则会遇到另一个问题:具有超过 22 个属性的元组。

    用于运行时的动态属性

    灵感来自 'Reads.traversableReads[F[_], A]'
    def attributesReads(required: Map[String, String]) = Reads {json =>
    type Errors = Seq[(JsPath, Seq[ValidationError])]

    def locate(e: Errors, idx: Int) = e.map { case (p, valerr) => (JsPath(idx)) ++ p -> valerr }

    required.map{
    case (key, "Double") => (__ \ key).read[Attribute[Double]](list(of[Double]).map(Attribute(key, _))).reads(json)
    case (key, "String") => (__ \ key).read[Attribute[String]](list(of[String]).map(Attribute(key, _))).reads(json)
    case (key, "Boolean") => (__ \ key).read[Attribute[Boolean]](list(of[Boolean]).map(Attribute(key, _))).reads(json)
    case _ => JsError("")
    }.iterator.zipWithIndex.foldLeft(Right(Vector.empty): Either[Errors, Vector[Attribute[_ >: Double with String with Boolean]]]) {
    case (Right(vs), (JsSuccess(v, _), _)) => Right(vs :+ v)
    case (Right(_), (JsError(e), idx)) => Left(locate(e, idx))
    case (Left(e), (_: JsSuccess[_], _)) => Left(e)
    case (Left(e1), (JsError(e2), idx)) => Left(e1 ++ locate(e2, idx))
    }
    .fold(JsError.apply, { res =>
    JsSuccess(res.toList)
    })
    }

    (__ \ "attributes").read(attributesReads(Map("keyA" -> "Double"))).reads(json)

    scala> json: play.api.libs.json.JsValue = {"attributes":{"keyA":[1.68,5.47,3.57],"keyB":[true],"keyD":["Lorem","Ipsum"]}}

    scala> res0: play.api.libs.json.JsResult[List[Attribute[_ >: Double with String with Boolean]]] = JsSuccess(List(Attribute(keyA,List(1.68, 5.47, 3.57))),/attributes)

    关于json - Play JSON : Reading and validating a JsObject with unknown keys,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36670756/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com