Search code examples
jsonscalasprayspray-json

Providing a JsonFormat for a Sequence of Objects


i`m trying here to find some help to apply an JsonFormat extended of the DefaultJsonProtocol to an class containing a Sequence of Objects.

So for the classes:

class Person(val name: String, [......], val adresses: Seq[Adress])
class Adress(val streetname: String, val plz: BigDecimal, val city: String)

now i would like to apply my JsonFormat:

object PersonJsonProtocol extends DefaultJsonProtocol {
  implicit object PersonJsonFormat extends RootJsonFormat[Person] {
    def write(pers: Person) = JsObject(
    "name" -> JsString(pers.name),
    [......],
    "adresses" -> JsArray(pers.adresses)
)
def read(value: JsValue) = {...}
}

But actually i am not sure how to do it. I searched through the spray-json documentation and throug google, stackoverflow & Co. I am totally new to Scala/Spray and perhaps i am just missing the point. So perhaps somebody here is so kind to help me. Without the Adress sequence i will work.

With the JsArray as provided in the example i get an type mismatch. It is exspecting an List[JsValue] but also with converting to list the mismatch still stands.

I also tried to inserts an seperate AdressJsonProtocol and include it via: "addresses" -> AdressJsonFormat.write(pers.adresses) but yet again it is an Sequence...


Solution

  • Look at the source of spray.json.CollectionFormats.

    Here is a runnable implementation:

    import spray.json._
    
    class Adress(val streetname: String, val plz: BigDecimal, val city: String)
    
    class Person(val name: String, val adresses: Seq[Adress])
    
    object PersonJsonProtocol extends DefaultJsonProtocol {
      implicit object AdressJsonFormat extends RootJsonFormat[Adress] {
        def write(addr: Adress) = JsObject(Map(
          "streetname" -> JsString(addr.streetname),
          "plz" -> JsNumber(addr.plz),
          "city" -> JsString(addr.city)
        ))
        def read(value: JsValue): Adress = ???
      }
      implicit object PersonJsonFormat extends RootJsonFormat[Person] {
        def write(pers: Person) = JsObject(Map(
          "name" -> JsString(pers.name),
          "adresses" -> JsArray(pers.adresses.map(_.toJson).toList)
        ))
        def read(value: JsValue): Person = ???
      }
    }
    
    object Main extends App {
      import PersonJsonProtocol._
      val person = new Person("joe", Seq(new Adress("street", 123, "city")))
      println("poso's default toString: %s".format(person))
      val personJVal = person.toJson
      println("JValue's toString: %s".format(personJVal))
      val personJson = personJVal.prettyPrint
      println("pretty-printing: %s".format(personJson))
    }
    

    which yields:

    poso's default toString: Person@680ccad
    JValue's toString: {"name":"joe","adresses":[{"streetname":"street","plz":123,"city":"city"}]}
    pretty-printing: {
      "name": "joe",
      "adresses": [{
        "streetname": "street",
        "plz": 123,
        "city": "city"
      }]
    }