Play框架和Scala Json,解析包含JSArray和JSObject的JSON

3
我的示例JSON包含一个国家对象。 JSON示例1:
  "@version": "1.0",
    "country": {
        "@country": "US",
        "day": {
            "@date": "2016-02-15",
            "@value": "1"
        }
    }

或者使用国家数组: Json示例2
"@version": "1.0",
    "country": [{
        "@country": "US",
        "day": {
            "@date": "2016-02-15",
            "@value": "1"
        }
    }, {
        "@country": "UK",
        "day": {
            "@date": "2016-02-15",
            "@value": "5"
        }]
    }

读取 JSON:

 implicit val dayJsonReads: Reads[DayJson] = (
      (JsPath \ "@date").read[DateTime](dateReads) and
        ((JsPath \ "@value").read[Int] orElse (JsPath \ "@value").read[String].map(_.toInt))
      )(DayJson.apply _)

    implicit val countryJsonReads: Reads[CountryJson] = (
      (JsPath \ "@country").read[String] and
        (JsPath \ "day").read[DayJson]
      )(CountryJson.apply _)

 implicit val newUserJsonReads: Reads[NewUserJson] = (
      (JsPath \ "@version").read[String] and
        (JsPath \ "country").readNullable[Seq[CountryJson]] 
      )(NewUserJsonParent.apply _)

以上代码读取了样例json 2,但对于样例json 1失败了。是否可以使用readNullable来读取JS Value或JS Object,或者我们可以将其从JS Value转换为JS Object。谢谢。

2个回答

4
你可以像这样做:

你可以像这样做:

object NewUserJson{
implicit val newUserJsonReads: Reads[NewUserJson] = (
  (JsPath \ "@version").read[String] and
    (JsPath \ "country").read[JsValue].map{
      case arr: JsArray => arr.as[Seq[CountryJson]]
      case obj: JsObject => Seq(obj.as[CountryJson])
    }
  )(NewUserJson.apply _)
}

这应该适用于这个 case class:
case class NewUserJson(`@version`: String, country: Seq[CountryJson])

但我不喜欢它,能否只使用相同的结构,当你只有一个国家时,发送一个仅包含一个国家的列表而不是对象?


谢谢你的回复。是的,我想把单个对象转换成列表。我可以这样写吗?object NewUserJson{implicit val newUserJsonReads: Reads[NewUserJson] = ((JsPath \ "@version").read[String] and (JsPath \ "country").read[JsValue].map{case arr: JsArray => arr.as[Seq[CountryJson]] case obj: JsObject => obj.as[Seq[CountryJson]]})(NewUserJson.apply _)} - binshi
对于上述问题,我一直收到“应用程序不接受参数错误”的提示。 - binshi
我的回答对你没用吗?当它是单个对象时,你不能将对象读取为Seq。 - Tomer
它没有工作。我应该也改变案例类类型吗? - binshi
我更新了答案,case class 应该是:case class NewUserJson(@version: String, country: Seq[CountryJson]) 如果这对你不起作用,我可以分享完整的示例和测试,以确保两个 JSON 都能通过。告诉我它是否对你有用。 - Tomer
谢谢您的回复。由于CountryJson有时可能为空,我必须使用readNullable。因此返回类型是Option [JsValue]。当我使用Option [JsArray]和Option [JsObject]时,它总是选择Option [JsArray]。 - binshi

1

在 Tomer 的解决方案上进行工作,以下是一个可用的示例。如果我能使它更加紧凑就好了。

案例类

case class NewUserJson(version: String, country: Option[Seq[CountryJson]])

Json解析对象。
 object NewUserJson{
    implicit val newUserJsonReads: Reads[NewUserJson] = (
      (JsPath \ "@version").read[String] and
        (JsPath \ "country").readNullable[JsValue].map {
          arr => {
            if (!arr.isEmpty){
              arr.get match {
                case arr: JsArray => Option(arr.as[Seq[CountryJson]])
                case arr: JsObject => Option(Seq(arr.as[CountryJson]))
              }
            }else {
              None
            }
          }
        }
      )(NewUserJson.apply _)
    }

1
Tomer编写了一个解决方案。如果您想使用nullable,可以修改他的代码为“readNullable [JsValue] .map {_.map {case arr:JsArray => ...}}” - andrey.ladniy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接