简体   繁体   中英

Play Framework 2.3 Scala - Serialize nested objects to JSon with implicit Writes converters

I need for a frontend jquery-component a specific json object like this (ajax response):

[
    {"division":"IT", "contacts":[
        {“firstname”:”Carl”, “surname”:”Smith”, “empID”:1},
        {“firstname”:”Henry”, “surname”:”Miller”, “empID”:2}
]}, 
    {"division":"Sales", "contacts":[
        {“firstname”:”Nancy”, “surname”:”McDonald”, “empID”:3},
        {“firstname”:”Susan”, “surname”:”McBright”, “empID”:4}
]}
]

In Backend the data is read via anorm (MySQL) and transformed in following object:

List(Map("division" -> "IT", "contacts" -> List(c3,c4)), Map("division" -> "Sales",          "contacts" -> List(c3,c4)))

Then I try to serialize the Object to JSon but without success (including implicit Writes converters). Below I made a simplified test case Idea-Worksheet in the same manner:

import play.api.libs.json.{JsValue, Json, Writes}

case class Contact(firstname: String, surname: String, empID: Option[Int])

case class ContactDivisionList(division: String, contacts: Seq[Contact])

implicit val ctWrites = new Writes[Contact] {
  def writes(ct: Contact) = Json.obj(
    "firstname" -> ct.firstname,
    "surname" -> ct.surname,
    "empid" -> ct.empID
  )
}

implicit val sdlWrites = new Writes[ContactDivisionList] {
  def writes(dlist: ContactDivisionList) = Json.obj(
    "division" -> dlist.division,
    "contacts" -> Json.toJson(dlist.contacts)
  )
}


/*
Example
*/
val c1 = Contact("Carl","Smith",Option(1))
val c2 = Contact("Henry","Miller",Option(2))
val c3 = Contact("Nancy","McDonald",Option(3))
val c4 = Contact("Susan","McBright",Option(4))

//Test case 1 ->OK
Json.toJson(List(c1,c2,c3,c4))

//Test case 2 ->OK
val c_comp1=List(Map("contacts" -> List(c1,c2)),Map("contacts" -> List(c3,c4)))
//RESULT --> c_comp1: List[scala.collection.immutable.Map[String,List[Contact]]] =     List(Map(contacts -> List(Contact(Carl,Smith,Some(1)), Contact(Henry,Miller,Some(2)))),   Map(contacts -> List(Contact(Nancy,McDonald,Some(3)), Contact(Susan,McBright,Some(4)))))
Json.toJson(c_comp1)
//res1: play.api.libs.json.JsValue = [{"contacts":    [{"firstname":"Carl","surname":"Smith","empid":1},{"firstname":"Henry","surname":"Miller","empid":2}]},{"contacts":[{"firstname":"Nancy","surname":"McDonald","empid":3},{"firstname":"Susan","surname":"McBright","empid":4}]}]


//Test case 3 ->Fail!!!
val c_comp2 = List(Map("division" -> "IT", "contacts" -> List(c1,c2)),Map("division" ->  "Sales", "contacts" -> List(c3,c4)))
//sdlWrites: play.api.libs.json.Writes[ContactDivisionList]{def writes(dlist:    ContactDivisionList): play.api.libs.json.JsObject} = $anon$2@3738baec

Json.toJson(c_comp2)
//!!!!!Error messages
/*Error:(39, 13) No Json serializer found for type     List[scala.collection.immutable.Map[String,java.io.Serializable]]. Try to implement an    implicit Writes or Format for this type.
Json.toJson(c_comp2)
^

Error:(39, 13) not enough arguments for method toJson: (implicit tjs:    play.api.libs.json.Writes[List[scala.collection.immutable.Map[String,java.io.Serializable]]    ])play.api.libs.json.JsValue.
Unspecified value parameter tjs.
Json.toJson(c_comp2)
^
*/

At the end of the script you can see "Test case 3" that got an error when i execute Json.toJson(c_comp2) -->"No Json serializer found for type..". I try a lot of things but i don't get it right. The only difference to successful "Test case 2" is that i extend the Map with a String-Tuppel.

I hope sombody can help me with that issue, Thx

Best regards Karsten

Your problem is that the Map you have there is mapping String to the least upper bound of String (your division name) and List[Contact] , which happens to be java.io.Serializable .

scala> case class Contact(firstname: String, surname: String, empID: Option[Int])
defined class Contact

scala> case class ContactDivisionList(division: String, contacts: Seq[Contact])
defined class ContactDivisionList

scala> val c1 = Contact("Carl","Smith",Option(1))
c1: Contact = Contact(Carl,Smith,Some(1))

scala> val c2 = Contact("Henry","Miller",Option(2))
c2: Contact = Contact(Henry,Miller,Some(2))

scala> val c3 = Contact("Nancy","McDonald",Option(3))
c3: Contact = Contact(Nancy,McDonald,Some(3))

scala> val c4 = Contact("Susan","McBright",Option(4))
c4: Contact = Contact(Susan,McBright,Some(4))

scala> Map("division" -> "IT", "contacts" -> List(c1,c2))
res10: scala.collection.immutable.Map[String,java.io.Serializable] = Map(division -> IT, contacts -> List(Contact(Carl,Smith,Some(1)), Contact(Henry,Miller,Some(2))))

I'm not entirely sure the nature of your problem, but if you already have List[ContactDivisionList] , it's pretty straightforward to serialize that to JSON:

scala> implicit val contactWrites = Json.writes[Contact]
contactWrites: play.api.libs.json.OWrites[Contact] = play.api.libs.json.OWrites$$anon$2@3676af92

scala> implicit val contactDivisionListWrites = Json.writes[ContactDivisionList]
contactDivisionListWrites: play.api.libs.json.OWrites[ContactDivisionList] = play.api.libs.json.OWrites$$anon$2@2999d17d

scala> Json.toJson(List(ContactDivisionList("IT", List(c1,c2)), ContactDivisionList("Sales", List(c3, c4))))
res2: play.api.libs.json.JsValue = [{"division":"IT","contacts":[{"firstname":"Carl","surname":"Smith","empID":1},{"firstname":"Henry","surname":"Miller","empID":2}]},{"division":"Sales","contacts":[{"firstname":"Nancy","surname":"McDonald","empID":3},{"firstname":"Susan","surname":"McBright","empID":4}]}]

It seems to me that you should avoid having that Map in the first place. I've never worked with anorm before, but I think where you need to be looking at is the code that data structure, because at that point, you've lost typesafety. You should ideally work with ContactDivisionList or construct your object using the JsValue cases directly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM