Search code examples
jsonscalaparsingtypesafe

Parsing a recursive trait playsafe json scala


I am using play-json_2.11, and I am trying to parse recursively some case classes

sealed trait Tree

case class Node(value: Float, child: Seq[Tree]) extends Tree

case class Leaf(leaf: Float) extends Tree

So basically, each Node contains a value and a list of Trees (which can be a Node or a Leaf).

So I am defining implicit readers in the companion objects of the case classes. and one in the object called Tree

object Node {
  implicit val reader = Json.reads[Node]
}

object Leaf {
  implicit val reader = Json.reads[Leaf]
}

object Tree {
  implicit val treeReads = 
    __.read[Node].map(x => x:Tree) orElse __.read[Leaf].map(x => x:Tree)
}

As the parsers are referencing to each other, I cannot define them and get the following error:

ScalaFiddle.scala:9: error: No instance of play.api.libs.json.Reads is available for scala.Seq[ScalaFiddle.Tree] in the implicit scope (Hint: if declared in the same file, make sure it's declared before)
implicit val reader = Json.reads[Node]

How can I parse a Tree in this case? (I do not need it to be specifically a Trait)

Here is the fiddle I tried https://scalafiddle.io/sf/sX8OkWI/3

My input is a json like this one

{
    "value": 1.0,
    "child": {
        "leaf": 2.0
    }
}

And I would like to parse it to have

Node(1.0, Leaf(2.0))

Solution

  • This is what you need

    import play.api.libs.json._
    import play.api.libs.functional.syntax._
    
    sealed trait Tree
    
    
    case class Node(value: Float, child: Tree) extends Tree
    object Node {
      implicit lazy val reader = Json.reads[Node]
    }
    
    case class Leaf(leaf: Float) extends Tree
    object Leaf {
      implicit lazy val reader = Json.reads[Leaf]
    }
    
    object Tree {
      implicit lazy val treeReads: Reads[Tree] = 
        __.lazyRead(Node.reader).map(x => x:Tree) orElse __.lazyRead(Leaf.reader).map(x => x:Tree)
    }
    
    
    val json: JsValue = Json.parse("""
    {
        "value": 5.0,
        "child": {
          "leaf": 7
        }
    }
    """)
    
    
    println(json)
    
    json.validate[Tree] match {
      case s: JsSuccess[Tree] => {
        val place: Tree = s.get
        println(place)
      }
      case e: JsError => {
        println(e)
      }
    }