1

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))
1
  • Recent version of play json directly support sealed family in Json.format macro Commented Jan 3, 2019 at 16:24

2 Answers 2

2

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)
  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Not sure you need lazyness for the Node and Leaf Reads
Yeah. Probably not
What would be the format to add implicit writes for Tree? I have an usecase where I need both reads and writes.
1

You don't need the implicits in the companion objects.. or objects for that matter:

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

sealed trait Tree

case class Node(value: Double, child: Tree) extends Tree
case class Leaf(leaf: Double) extends Tree

val json: JsValue = Json.parse("""
{
    "value": 1.0,
    "child": {
      "leaf": 2.0
    }
}
""")

implicit val nReader = Json.reads[Node]
implicit val lReader = Json.reads[Leaf]
implicit lazy val treeReads: Reads[Tree] = 
    __.lazyRead(nReader).map(x => x:Tree) orElse __.lazyRead(lReader).map(x => x:Tree)

json.validate[Tree] match {
  case s: JsSuccess[Tree] => {
    val place: Tree = s.get
    println(place)
  }
  case e: JsError => {
    println(e)
  }
}

https://scalafiddle.io/sf/sX8OkWI/13

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.