0

I have reached a point where my code compiles successfully, yet I have doubts about my solution and am posting this question for that reason.

I have a Node class defined as:

case class Node(id: Long, label: String, parent_id: Option[Long])

The reason I quote/unquote recursive is because technically, I do not store a Node within a Node. Rather, each node has a pointer to its parent and I can say: give me all children of Node id=X.

Here is an example tree, for the sake of visualization. I would like to give the root_node's ID, and obtain the tree's conversion to a Json String:

root_node
|_ node_1
|  |_ node_11
|     |_ node_111
|_ node_2
|_ node_3

The Json would look like:

{"title": "root_node", "children": [...]}

with the children array containing node_1, 2 and 3 etc... recursively

Here is the Writes Converter for Node:

/** json converter of Node to JSON */
implicit val NodeWrites = new Writes[Node] {
  def writes(node: Node) = Json.obj(
    "title"  -> node.label,
    "children" -> Node.getChildrenOf(node.id)
  )
}

Quoting Play docs:

The Play JSON API provides implicit Writes for most basic types, such as Int, Double, String, and Boolean. It also supports Writes for collections of any type T that a Writes[T] exists.

I need to point out that Node.getChildrenOf(node.id) returns a List of Nodes from the DB. So according to Play's docs, I should be able to convert a List[Node] to Json. It seems that doing this within the Writes converter itself is a bit more troublesome.

Here is the resulting error from running this code:

type mismatch;
 found   : List[models.Node]
 required: play.api.libs.json.Json.JsValueWrapper
 Note: implicit value NodeWrites is not applicable here because it comes after the application point and it lacks an explicit result type

I added the "explicit result type" to my Writes converter, here is the result:

/** json converter of Node to JSON */
implicit val NodeWrites: Writes[Node] = new Writes[Node] {
  def writes(node: Node) = Json.obj(
    "title"  -> node.label,
    "children" -> Node.getChildrenOf(node.id)
  )
}

The code now executes properly, I can visualize the tree on the browser.

Even though this looks to me like the cleanest working solution, IntelliJ still complains about the line:

"children" -> Node.getChildrenOf(node.id)

saying:

Type mismatch: found(String, List[Node]), required (String, Json.JsValueWrapper)

Could it be that IntelliJ's error reporting is not based off of the Scala compiler?

Finally, is the overall approach of the JSON converter terrible?

Thanks and sorry for the long post.

1 Answer 1

1

The problem lies in "children" -> Node.getChildrenOf(node.id). Node.getChildrenOf(node.id) returns a List[Node]. Whereas any attribute in the Json.obj expects JsValueWrappers. In this case a JsArray.

Something like this should work:

implicit val writes = new Writes[Node] {
  def writes(node: Node) = Json.obj(
    "title" -> node.label, 
    // Note that we have to pass this since the writes hasn't been defined just yet.
    "children" -> JsArray(Node.getChildrenOf(node).map(child => Json.toJson(child)(this)))
  )
}

This at least compiles, I haven't tested it with any data though.

Sign up to request clarification or add additional context in comments.

2 Comments

This fixed it! Though would you please explain what "(this)" does? I tried removing it and it is still working.
If you take a look at the definition of Json.toJson, you'll notice that this function has an additional implicit parameter. Usually, this parameter is filled in by the compiler. It provides the toJson function with an implementation to convert the first parameter to a Json object. I tried it once with play 2.3.10, and it didn't seem to compile without the (this) addition. In case it does work in your case, I would just leave it out.

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.