score:2

The "addresses" in User contains the address so you don't really need to send Map[User, List[Address]] back to client. The Json would be an array of serialized user objects and addresses is part of that. If you do want to send back a Map then the type Map[String, List[Address]] makes more sense in Json serialization context. Here is the code to generate Json for List[User]. The output looks like this

[
{
    "id": 1,
    "name": "John Doe",
    "email": "john@email.com",
    "addresses": [
        {
            "id": 1001,
            "userId": 1,
            "city": "Chicago"
        },
        {
            "id": 1002,
            "userId": 1,
            "city": "New York"
        }
    ]
},
{
    "id": 2,
    "name": "Jane Doe",
    "email": "jane@email.com",
    "addresses": [
        {
            "id": 1012,
            "userId": 1,
            "city": "Dallas"
        }
    ]
}

]

Here is the code the would be in you controller. It has implicit Json formatters that are used by Json.toJson.

      implicit object PkWrites extends Writes[Pk[Long]] {
    def writes(key: Pk[Long]) = Json.toJson(key.toOption)
  }

  implicit object PkReads extends Reads[Pk[Long]] {
    def reads(json: JsValue) = json match {
      case l: JsNumber => JsSuccess(Id(l.value.toLong))
      case _ => JsSuccess(NotAssigned)
    }
  }

  implicit val AddressWrites: Writes[Address] = (
    (JsPath \ "id").write[Pk[Long]] and
      (JsPath \ "userId").write[Long] and
      (JsPath \ "city").write[String]
    )(unlift(Address.unapply))

  implicit val AddressReads: Reads[Address] = (
    (JsPath \ "id").read[Pk[Long]] and
      (JsPath \ "userId").read[Long] and
      (JsPath \ "city").read[String]
    )(Address.apply _)

  implicit val UserWrites: Writes[User] = (
    (JsPath \ "id").write[Pk[Long]] and
      (JsPath \ "name").write[String] and
      (JsPath \ "email").write[String] and
      (JsPath \ "addresses").write[List[Address]]
    )(unlift(User.unapply))

  def makeJson() = Action {
    val johnAddr1 = Address(Id(1001), 1, "Chicago")
    val johnAddr2 = Address(Id(1002), 1, "New York")
    val janeAddr1 = Address(Id(1012), 1, "Dallas")
    val john = User(Id(1), "John Doe", "john@email.com", List(johnAddr1, johnAddr2))
    val jane = User(Id(2), "Jane Doe", "jane@email.com", List(janeAddr1))
    Ok(Json.toJson(List(john, jane)))
    //    Ok(Json.toJson(map))
  }

score:4

You can solve this problem by transforming your Map[User, List[Address]] to a List[User], and the JsonWriter will became easy to write.

Something like:

list.map {
  case (user, address) => user.copy(addresses = address.toSeq)
}

score:5

That should help

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


case class User(
  id: Pk[Long] = NotAssigned,
  name: String = "",
  email: String = "",
  addresses: Seq[Address])

case class Address(
  id: Pk[Long] = NotAssigned,
  userId: Long,
  city: String)

// Play does not provide Format[Pk[A]], so you need to define it
implicit def pkReads[A](implicit r: Reads[Option[A]]): Reads[Pk[A]] = r.map {  _.map(Id(_)).getOrElse(NotAssigned)  }
implicit def pkWrites[A](implicit w: Writes[Option[A]]): Writes[Pk[A]] =  Writes(id => w.writes(id.toOption))

implicit val addrFormat = Json.format[Address]
implicit val userFormat = Json.format[User]

Now you can easily serialize a user:

val as = Seq(Address(Id(2), 1, "biim"))
val u = User(Id(1), "jto", "jto@foo.bar", as)

scala> Json.toJson(u)
res6: play.api.libs.json.JsValue = {"id":1,"name":"jto","email":"jto@foo.bar","addresses":[{"id":2,"userId":1,"city":"biim"}]}

As Julien says, you can't just serialize a Map[User, Seq[Address]]. It just does not make sense since User can't be a key in a Json Object.


Related Query

More Query from same tag