Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read JSON Tree structure in Scala Play Framework

I am trying to process an Ajax POST request in Play Framework 2.1.3. The post data is a JSON object and has a tree structure like:

{ id: "a", name: "myname", kids : [{ id: "a1", name : "kid1", kids: []}, {id: "a2", name: "kid2", kids: [{id: "aa1", name :"grandkid", kids: []}]}]

I would like to nest the 'children' arbitrarily deep. The class I would have in mind is like this (I realize the recursiveness can be problematic):

case class Person {
  id: String,
  name: String,
  kids: Array[Person]
}

The format I would have in mind:

implicit val personFormat:Format[Person] = Json.format[Person]

Play is throwing errors on my Format that I wrote:

type mismatch; found : controllers.Resources.Person required: Array[controllers.Resources.Person]

I am aware that Play has a Tree structure. I couldn't find examples/documentation on how to tie that to JSON reads.

Any help is highly appreciated, thanks

like image 813
Joost Avatar asked Oct 26 '13 17:10

Joost


2 Answers

You will need a recursive val, something like:

implicit val jsonReads: Reads[Person] = ((__ \ "id").read[String] and (__ \ "name").read[String] and (__ \ "kids").read[Seq[Person]])(apply _)

(I've changed the collection type from Array to Seq because it's more general and allows you to change your implementation without affecting downline code.)

This is using the syntax documented here.

like image 127
Robin Green Avatar answered Nov 13 '22 04:11

Robin Green


The only way that I see this working is using either JsArray or Array[String] instead of Array[Person] in your Person case class. The JSON Macro Inception can only generate reads + writes code for primitives and lists, maps, and arrays for objects for which there already exist implicit JSON read + write code. Essentially you can't have a case class that references itself.

package models

import play.api.libs.json._

case class Person(
  id   : String,
  name : String,
  kids : JsArray
)

object Person extends ((String,String,JsArray) => Person) {

  implicit val jsonFormat = Json.format[Person]

}
like image 2
Jason Pearson Avatar answered Nov 13 '22 05:11

Jason Pearson