Play Framework 和 Scala Json,解析 json 包含 JSArray 和 JSObject
Play Framework and Scala Json, parsing for json containing JSArray and JSObject
我的示例 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 值或 JS 对象,或者我们可以将它从 JS 值转换为对象。谢谢。
你可以这样做:
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 _)
}
这应该适用于这种情况 class:
case class NewUserJson(`@version`: String, country: Seq[CountryJson])
但我不喜欢,你不能只使用相同的结构,当你只有一个国家时,只发送一个只包含一个国家的列表,而不是对象吗?
使用 Tomer 的解决方案,下面是一个工作示例。如果我能把它做得更紧凑就好了。
案例class
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 _)
}
我的示例 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 值或 JS 对象,或者我们可以将它从 JS 值转换为对象。谢谢。
你可以这样做:
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 _)
}
这应该适用于这种情况 class:
case class NewUserJson(`@version`: String, country: Seq[CountryJson])
但我不喜欢,你不能只使用相同的结构,当你只有一个国家时,只发送一个只包含一个国家的列表,而不是对象吗?
使用 Tomer 的解决方案,下面是一个工作示例。如果我能把它做得更紧凑就好了。
案例class
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 _)
}