Рекурсивный кейс-класс play json lazyRead
В JsPath.scala
описание lazyRead
Метод заключается в следующем:
/*case class User(id: Long, name: String, friend: User)
implicit lazy val UserReads: Reads[User] = (
(__ \ 'id).read[Long] and
(__ \ 'name).read[String] and
(__ \ 'friend).lazyRead(UserReads)
)(User.apply _) */
def lazyRead[T](r: => Reads[T]): Reads[T] = Reads(js => Reads.at[T]
(this)(r).reads(js))
когда я пытаюсь выполнить упрощенную версию приведенного выше примера:
case class User(id:Int, user: User)
val userJsVal = Json.parse(
"""
| {
| "id" : 22,
| "user" : { "id":2 }
| }
""".stripMargin)
implicit lazy val UserReads: Reads[User] = (
(__ \ 'id).read[Int] and
( __ \ 'user).lazyRead(UserReads)
)(User.apply _)
val us = Json.fromJson[User](userJsVal)(UserReads);
us match {
case s:JsSuccess[User] => println(s.get)
case e:JsError => println(JsError.toJson(e).toString()) }
Я получаю ошибку:
{"obj.user.user":[{"msg":["error.path.missing"],"args":[]}]}
Я попытался установить внутреннее значение "пользователь" на ноль, но это тоже не сработало. Как я должен создать свой JSON или добавить условия завершения в:
( __ \ 'user).lazyRead(UserReads)
получить действительный десериализованный вывод?
2 ответа
Если вы не ограничены в выборе JSON-парсеров, попробуйте jsoniter-scala
- он имеет встроенную поддержку рекурсивных структур и многих других полезностей, таких как лучшие характеристики производительности по сравнению с другими парсерами JSON для Scala.
Как уже упоминалось @Andriy, ваш пользовательский объект должен иметь Option[User], иначе ваш внутренний объект всегда должен иметь другого пользователя до бесконечности. И вы можете заметить, что в вашем примере JSON у внутреннего пользователя уже нет другого пользователя под ним. Затем, после того как вы измените внутреннего пользователя на необязательный, вы должны использовать lazyReadNullable, вот ваш пример исправления:
import play.api.libs.json.{JsError, JsSuccess, Json, Reads}
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class User(id:Int, user: Option[User])
val userJsVal = Json.parse(
"""
| {
| "id" : 22,
| "myuser" : { "id":2 }
| }
""".stripMargin)
implicit lazy val UserReads: Reads[User] = (
(__ \ 'id).read[Int] and
( __ \ 'myuser).lazyReadNullable(UserReads)
)(User.apply _)
val us = Json.fromJson[User](userJsVal)(UserReads)
us match {
case s:JsSuccess[User] => println(s.get)
case e:JsError => println(JsError.toJson(e).toString()) }