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"}