0

I have 3 case classes which are having cyclic reference for nested field each other as below.

case class DataSource(subQuery: Query,name:String)
case class JoinQuery(joinType:String,query:Query)
case class Query(child:DataSource, joinQuery:Seq[JoinQuery])

I want to write a companion class for these with Json formater class to parse json to case class and vice versa. How can I achieve this ? I tried the answer mentioned below but with no luck.

cchantep
  • 9,118
  • 3
  • 30
  • 41
Bill Goldberg
  • 1,699
  • 5
  • 26
  • 50
  • 1
    Can you clarify what you'd like to have as JSON serialized form? Do you need to handle deserialization as well? Also what did you try so far and what error did you get? – Gaël J Feb 28 '23 at 19:59

1 Answers1

0

I tried the answer mentioned below but with no luck.

Sorry but this is not a proper description of what you did and what error you got.

I didn't manage to make automated mappings work without NullPointerExceptions but the approach with recursive types seems to work.

DataSource depends on Query and Query depends on DataSource, so it's not clear how you're going to instantiate these classes. Probably with nulls, so at least some of the fields should be optional. For example I modified this place: case class Query(child:Option[DataSource], ...)

import play.api.libs.json._
import play.api.libs.functional.syntax._

case class DataSource(subQuery: Query,name:String)
object DataSource {
  implicit lazy val reads: Reads[DataSource] = (
    (__ \ "subQuery").lazyRead(Reads.of[Query](Query.reads)) and
      (__ \ "name").read[String]
    )(DataSource.apply _)

  implicit lazy val writes: Writes[DataSource] = (
    (__ \ "subQuery").lazyWrite(Writes.of[Query](Query.writes)) and
      (__ \ "name").write[String]
    )(unlift(DataSource.unapply))
}

case class JoinQuery(joinType:String,query:Query)
object JoinQuery {
  implicit lazy val reads: Reads[JoinQuery] = (
    (__ \ "joinType").read[String] and
      (__ \ "query").lazyRead(Reads.of[Query](Query.reads))
    )(JoinQuery.apply _)

  implicit lazy val writes: Writes[JoinQuery] = (
    (__ \ "joinType").write[String] and
      (__ \ "query").lazyWrite(Writes.of[Query](Query.writes))
    )(unlift(JoinQuery.unapply))
}

case class Query(child:Option[DataSource], joinQuery:Seq[JoinQuery])
object Query {
  implicit lazy val reads: Reads[Query] = (
    (__ \ "child").lazyRead(Reads.optionWithNull[DataSource](DataSource.reads)) and
      (__ \ "joinQuery").lazyRead(Reads.seq[JoinQuery](JoinQuery.reads))
    )(Query.apply _)

  implicit lazy val writes: Writes[Query] = (
    (__ \ "child").lazyWrite(Writes.optionWithNull[DataSource](DataSource.writes)) and
      (__ \ "joinQuery").lazyWrite(Writes.seq[JoinQuery](JoinQuery.writes))
    )(unlift(Query.unapply))
}

Json.parse(
  """{"subQuery":{"child":{"subQuery":{"child":null,"joinQuery":[]},"name":"b"},"joinQuery":[{"joinType":"c","query":{"child":null,"joinQuery":[]}}]},"name":"a"}"""
).as[DataSource])
// DataSource(Query(Some(DataSource(Query(None,List()),b)),List(JoinQuery(c,Query(None,List())))),a)

Json.stringify(Json.toJson(
  DataSource(Query(Some(DataSource(Query(None, Seq()), "b")), Seq(JoinQuery("c", Query(None, Seq())))), "a")
))
// {"subQuery":{"child":{"subQuery":{"child":null,"joinQuery":[]},"name":"b"},"joinQuery":[{"joinType":"c","query":{"child":null,"joinQuery":[]}}]},"name":"a"}
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66