play-json:如何获得" dynamic"密钥使用Reads [T]?

时间:2015-10-22 12:32:34

标签: json scala playframework

我有SeqJsValue元素。每个元素代表以下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 _)

或者必须这样做"手动"如果是,怎么做?

2 个答案:

答案 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在其他情况下是否对您有用。