Условная фильтрация JSON перед десериализацией в модель класса случая - PullRequest
3 голосов
/ 14 июня 2019

Как условно проанализировать 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]]

но иногда веб-служба возвращает 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?

Ответы [ 2 ]

3 голосов
/ 14 июня 2019

Чтобы не изменять модель на необязательные поля, мы могли бы определить «от побережья к побережью» трансформатор , который отфильтровывает пользователей с отсутствующими датами, например

    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)
1 голос
/ 14 июня 2019

Вы можете использовать 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 не предоставлены.

...