4

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...

Klink
  • 145
  • 2
  • 11

2 Answers2

9

You don't need to write a DefaultJsonProtocol for each case class, except if you want some special logic (formatting, filtering ...)

Have you tried to simply use the default case class serialization?

implicit val formatPerson = jsonFormat6(Adress)
implicit val formatAddress = jsonFormat3(Adress)

The number in jsonFormat'number' stands for the number of members in your case class.

Then spray-json will take care of your nested Address collection when serializing a Person.

Arnaud Gourlay
  • 4,646
  • 1
  • 29
  • 35
  • 1
    Thank you i am sure this would work too but i wanted to use the DefaultJsonProtocol to access the Json names and provided values directly and seperate from the original class. I should have stated this in the original post. Sorry i would like to vote your answer up but i ddon't have enough reputation yet. But thank you also! – Klink Feb 13 '14 at 19:59
  • @Klink, What do you mean when you say: "i wanted to use the DefaultJsonProtocol to access the Json names and provided values directly and seperate from the original class"? – Dave Swartz Feb 19 '14 at 05:10
  • @DaveSwartz Sorry for being unclear again. So what i meant was that the extension of the DefaultJsonProtocol allows me to handle the information provided in the resulting Json. For Example: "name" -> JsString(pers.name) could also be "name" -> JsString(pers.name+", "+pers.title) or "person-name" -> JsString(pers.name) – Klink Feb 19 '14 at 09:10
  • While looking at your answer I realized that I used wrong number after the "jsonFormat" in my code. – Display Name Feb 20 '15 at 19:15
3

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"
  }]
}
Rob Starling
  • 3,868
  • 3
  • 23
  • 40
  • Thank you the Hint with "spray.json.CollectionFormats" and JsArray(pers.adresses.map(_.toJson).toList) did the trick! Thank you so much! – Klink Feb 13 '14 at 19:57