Рекурсивный кейс-класс 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()) }
Другие вопросы по тегам