我有Seq
个JsValue
元素。每个元素代表以下JSON结构,包含两个字段:
{
"name": "xy"
"key ∈ {A,B,C}": ["// some values in an array"]
}
这意味着我知道第一个字段的键(始终"名称"),但不知道数组的键,因为它是" dynamic"。但是:可能的键是已知的,它是" A"," B"或" C"。
我想要做的是将每个JsValue
对象映射到案例类:
case class Element(name: String, values: Seq[String])
如您所见,动态密钥的名称甚至不重要。我只想获得与之关联的数组。
但是:如果数组的密钥不同,怎么能用Reads[T]
获取数组?
implicit val reads: Reads[Element] = (
(__ \ "name").read[String] and
(__ \ "???").read[Seq[String]]
)(Element.apply _)
或者必须这样做"手动"如果是,怎么做?
答案 0 :(得分:1)
您可以使用orElse
方法
case class Element(name: String, values: Seq[String])
object Element {
implicit val reads: Reads[Element] = (
(__ \ "name").read[String] and
(__ \ "a").read[Seq[String]]
.orElse((__ \ "b").read[Seq[String]])
.orElse((__ \ "c").read[Seq[String]])
)(Element.apply _)
}
答案 1 :(得分:1)
正如其他答案所指出的那样,orElse
可以在这里工作,但是如果你想要更多的灵活性,你总是可以编写类似于返回Reads
的方法来寻找满足某些谓词的键: / p>
import play.api.libs.json._
def findByKey[A: Reads](p: String => Boolean): Reads[A] = Reads[A] {
case JsObject(fields) => fields.find(kv => p(kv._1)).map(
_._2.validate[A]
).getOrElse(JsError("No valid field key"))
case _ => JsError("Not an object")
}
然后:
import play.api.libs.functional.syntax._
case class Element(name: String, values: Seq[String])
object Element {
implicit val reads: Reads[Element] = (
(__ \ "name").read[String] and findByKey[Seq[String]](Set("A", "B", "C"))
)(Element.apply _)
}
最后:
scala> Json.parse("""{ "name": "foo", "A": ["bar", "baz"] }""").asOpt[Element]
res0: Option[Element] = Some(Element(foo,List(bar, baz)))
scala> Json.parse("""{ "name": "foo", "A": [1, 2] }""").asOpt[Element]
res1: Option[Element] = None
您选择哪种方法是一种品味问题,并且可能部分取决于更一般的findByKey
在其他情况下是否对您有用。