【发布时间】:2016-04-19 21:59:10
【问题描述】:
我正在阅读使用多个 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]],在转换整个文档时可用于“属性”字段(请记住,“属性”只是一个子文档)。
然后有一个简单的映射,其中包含应用于验证属性的键和数组类型的允许组合。编辑:此地图特定于每个请求(或者更确切地说,特定于每种类型的 json 文档)。但是你可以假设它在作用域中总是可用的。
val required = Map(
"KeyA" -> "Double",
"KeyB" -> "String",
"KeyD" -> "String",
)
所以在上面显示的 JSON 的情况下,Reads 应该会产生两个错误:
- “keyB”确实存在,但类型错误(应为字符串,为布尔值)。
- “keyD”缺失(而 keyC 不需要,可以忽略)。
我无法创建必要的Reads。作为第一步我尝试的第一件事,从外部Reads的角度来看:
...
(__ \ "attributes").reads[Map[String, JsArray]]...
...
我认为这是很好的第一步,因为如果 JSON 结构不是包含 Strings 和 JsArrays 作为键值对的对象,那么 Reads 将失败并显示正确的错误消息。它有效,但是:我不知道如何从那里继续。当然我可以创建一个将Map 转换为Seq[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 中手动验证地图的每个元素。但我认为这太复杂了。任何帮助表示赞赏。
编辑澄清:
在文章的开头我说过键(keyA,keyB...)在编译时是未知的。后来我说这些键是映射required 的一部分,用于验证。这听起来很矛盾,但问题是:required 特定于每个文档/请求,并且在编译时也不知道。但是您不必担心这一点,只需假设对于每个请求,正确的required 已经在范围内可用。
【问题讨论】:
标签: json scala playframework playframework-2.0