在反序列化为 case class 模型之前对 JSON 进行条件过滤
Conditional filtering of JSON before deserialisation to case class model
如何在反序列化为以下情况 class 之前有条件地解析 JSON:
case class UserInfo(id: String, startDate: String, endDate: String)
我有隐式读取
object UserInfo {
implicit val reads: Reads[UserInfo] = (
(__ \ "id").read[String] and
(__ \ "startDate").read[String] and
(__ \ "endDate").read[String]
)(UserInfo.apply _)
}
我可以使用上面的隐式读取
解析以下json
val jsonString = """
{
"users":[
{
"id":"123",
"startDate":"2019-06-07",
"endDate":"2019-06-17"
},
{
"id":"333",
"startDate":"2019-06-07",
"endDate":"2019-06-27"
}
]
}"""
val userInfoList = (Json.parse(jsonString) \ "users").as[List[UserInfo]]
但有时 Web 服务 returns 是 JSON,没有 startDate
和 endDate
,例如:
{
"users":[
{
"id":"123",
"startDate":"2019-06-07",
"endDate":"2019-06-17"
},
{
"id":"333",
"startDate":"2019-06-07"
},
{
"id":"444"
}
]
}
如何有条件地解析 json 以忽略没有 startDate
或 endDate
的对象,而不使这些字段在 UserInfo
模型中成为可选字段?
您可以为此使用 Option
:
case class UserInfo(id: String, startDate: Option[String], endDate: Option[String])
object UserInfo {
implicit val reads: Reads[UserInfo] = (
(__ \ "id").read[String] and
(__ \ "startDate").readNullable[String] and
(__ \ "endDate").readNullable[String]
)(UserInfo.apply _)
}
当未提供 startDate
和 endDate
时,这将起作用。
为了避免将模型更改为可选字段,我们可以定义 coast-to-coast transformer 过滤掉缺少日期的用户,例如
val filterUsersWithMissingDatesTransformer = (__ \ 'users).json.update(__.read[JsArray].map {
case JsArray(values) => JsArray(values.filter { user =>
val startDateOpt = (user \ "startDate").asOpt[String]
val endDateOpt = (user \ "endDate").asOpt[String]
startDateOpt.isDefined && endDateOpt.isDefined
})
})
给定
val jsonString =
"""
|{
| "users":[
| {
| "id":"123",
| "startDate":"2019-06-07",
| "endDate":"2019-06-17"
| },
| {
| "id":"333",
| "startDate":"2019-06-07"
| },
| {
| "id":"444"
| }
| ]
|}
""".stripMargin
val filteredUsers = Json.parse(jsonString).transform(filterUsersWithMissingDatesTransformer)
println(filteredUsers.get)
产出
{
"users": [
{
"id": "123",
"startDate": "2019-06-07",
"endDate": "2019-06-17"
}
]
}
意味着我们可以反序列化到现有模型,而无需使 startDate
和 endDate
可选。
case class UserInfo(id: String, startDate: String, endDate: String)
如何在反序列化为以下情况 class 之前有条件地解析 JSON:
case class UserInfo(id: String, startDate: String, endDate: String)
我有隐式读取
object UserInfo {
implicit val reads: Reads[UserInfo] = (
(__ \ "id").read[String] and
(__ \ "startDate").read[String] and
(__ \ "endDate").read[String]
)(UserInfo.apply _)
}
我可以使用上面的隐式读取
解析以下json val jsonString = """
{
"users":[
{
"id":"123",
"startDate":"2019-06-07",
"endDate":"2019-06-17"
},
{
"id":"333",
"startDate":"2019-06-07",
"endDate":"2019-06-27"
}
]
}"""
val userInfoList = (Json.parse(jsonString) \ "users").as[List[UserInfo]]
但有时 Web 服务 returns 是 JSON,没有 startDate
和 endDate
,例如:
{
"users":[
{
"id":"123",
"startDate":"2019-06-07",
"endDate":"2019-06-17"
},
{
"id":"333",
"startDate":"2019-06-07"
},
{
"id":"444"
}
]
}
如何有条件地解析 json 以忽略没有 startDate
或 endDate
的对象,而不使这些字段在 UserInfo
模型中成为可选字段?
您可以为此使用 Option
:
case class UserInfo(id: String, startDate: Option[String], endDate: Option[String])
object UserInfo {
implicit val reads: Reads[UserInfo] = (
(__ \ "id").read[String] and
(__ \ "startDate").readNullable[String] and
(__ \ "endDate").readNullable[String]
)(UserInfo.apply _)
}
当未提供 startDate
和 endDate
时,这将起作用。
为了避免将模型更改为可选字段,我们可以定义 coast-to-coast transformer 过滤掉缺少日期的用户,例如
val filterUsersWithMissingDatesTransformer = (__ \ 'users).json.update(__.read[JsArray].map {
case JsArray(values) => JsArray(values.filter { user =>
val startDateOpt = (user \ "startDate").asOpt[String]
val endDateOpt = (user \ "endDate").asOpt[String]
startDateOpt.isDefined && endDateOpt.isDefined
})
})
给定
val jsonString =
"""
|{
| "users":[
| {
| "id":"123",
| "startDate":"2019-06-07",
| "endDate":"2019-06-17"
| },
| {
| "id":"333",
| "startDate":"2019-06-07"
| },
| {
| "id":"444"
| }
| ]
|}
""".stripMargin
val filteredUsers = Json.parse(jsonString).transform(filterUsersWithMissingDatesTransformer)
println(filteredUsers.get)
产出
{
"users": [
{
"id": "123",
"startDate": "2019-06-07",
"endDate": "2019-06-17"
}
]
}
意味着我们可以反序列化到现有模型,而无需使 startDate
和 endDate
可选。
case class UserInfo(id: String, startDate: String, endDate: String)