play-json-derived-codecs icon indicating copy to clipboard operation
play-json-derived-codecs copied to clipboard

Recursive OFormat problem (not sure if this is a limit in the language or play or your library)

Open jarlah opened this issue 8 years ago • 4 comments

neither

sealed trait SillyTrait

case class SillyObject(silly: SillyObject) extends SillyTrait

object SillyTrait {
  implicit lazy val format: OFormat[SillyTrait] = flat.oformat((__ \ "type").format[String])
}

or

sealed trait SillyTrait

case class SillyObject(silly: SillyObject) extends SillyTrait

object SillyObject {
  implicit lazy val format = Json.format[SillyObject]
}

object SillyTrait {
  implicit lazy val format: OFormat[SillyTrait] = flat.oformat((__ \ "type").format[String])
}

or

sealed trait SillyTrait

case class SillyObject(silly: SillyObject) extends SillyTrait

object SillyObject {
  implicit lazy val format = Json.format[SillyObject]
}

can be used as a representation for a json payload where the type SillyObject can be nested within SillyObject.

Is this a known limitation with Scala or Play? Can this be solved by adding extra magic with play-son-derived-codecs?

jarlah avatar Jun 29 '16 14:06 jarlah

Its fully possible to do manually with laziness as described in http://stackoverflow.com/questions/27443335/scala-play-2-3-framework-json-validate-recursively extract:

(which is also described in official play doc: https://www.playframework.com/documentation/2.5.x/ScalaJsonCombinators#recursive-types)

case class RuleJson(
  `type`: String,
  attribute: Option[String],
  operator: Option[String],
  value: String,
  isValueProcessed: Option[Boolean],
  aggregator: Option[String],
  conditions: Seq[RuleJson]
)

object RuleJson {
  implicit val reads: Reads[RuleJson] = (
    (__ \ "type").read[String] and
    (__ \ "attribute").readNullable[String] and
    (__ \ "operator").readNullable[String] and
    (__ \ "value").read[String] and
    (__ \ "is_value_processed").readNullable[Boolean] and
    (__ \ "aggregator").readNullable[String] and
    (__ \ "conditions").lazyReadNullable[Seq[RuleJson]](Reads.seq(RuleJson.reads))
      .map(opt => opt.toSeq.flatten)
  )(RuleJson.apply _)
}

Shouldn't either the play library or this library be capable of figuring out the recursive formats with laziness?

jarlah avatar Jun 29 '16 15:06 jarlah

Hm, the first snippet of code should work as is. I will investigate why it doesn’t.

In the meantime, it seems that you can workaround the issue as follows:

sealed trait SillyTrait

case class SillyObject(silly: Option[SillyObject]) extends SillyTrait

object SillyObject {
  implicit val format: OFormat[SillyObject] = flat.oformat((__ \ "type").format[String])
}

object SillyTrait {
  implicit lazy val format: OFormat[SillyTrait] = flat.oformat((__ \ "type").format[String])
}

julienrf avatar Jun 30 '16 20:06 julienrf

Thanks for reporting that, by the way!

julienrf avatar Jun 30 '16 20:06 julienrf

The last example you gave (RuleJson) should work.

Here, the problem is that the recursivity is on a leaf, and not on the hierarchy root type. I’m still thinking of a better derivation mechanism to handle that case… Stay tuned!

julienrf avatar Jul 01 '16 10:07 julienrf