5

I am trying to write a json reads combinator for type Map[Int, Long]

I have this so far:

implicit val mapWrites = Json.writes[Map[Int, Long]]

implicit val mapReads: Reads[Map[Int, Long]] = (
  // ???
) // ?

I'm not sure how this will work, I tried doing (Map[Int, Long].apply but that apply method was not available.

Looking for some help to write this Reads combinator.

acjay
  • 34,571
  • 6
  • 57
  • 100
Blankman
  • 259,732
  • 324
  • 769
  • 1,199

1 Answers1

8

This should do it, with the caveat that it doesn't handle NumberFormatException in the reads case:

//
// scala> Json.toJson(Map(1 -> 2L, 2 -> 3L))
// res0: play.api.libs.json.JsValue = {"1":2,"2":3}
//
implicit val formatter: Format[Map[Int, Long]] = {
  new Format[Map[Int, Long]] {
    def writes(m: Map[Int, Long]) = {
      Json.toJson(m.map {
        case (key, value) => key.toString -> value
      })
    }

    def reads(json: JsValue) = {
      json.validate[Map[String, Long]].map(_.map {
        case (key, value) => key.toInt -> value
      })
    }
  }
}

As separate Reads and Writes instances:

implicit val readsInstance: Reads[Map[Int, Long]] = {
  new Reads[Map[Int, Long]] {
    def reads(json: JsValue) = {
      json.validate[Map[String, Long]].map(_.map {
        case (key, value) => key.toInt -> value
      })
    }
  }
}

implicit val writesInstance: Writes[Map[Int, Long]] = {
    def writes(m: Map[Int, Long]) = {
      Json.toJson(m.map {
        case (key, value) => key.toString -> value
      })
    }
  }
}
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
  • How does this Format differ from what I did above? Can your code snippet be written as 2 seperate implicits Reads and Writes? – Blankman Feb 23 '15 at 16:46
  • 1
    @Blankman `Format` is just `Reads` and `Writes`, so you can split it in two, if that's your preference, but I tend to use `Format` when I need to implement both traits. https://github.com/playframework/playframework/blob/df5a65fb426ae9d10052cfeacfb7a3c1080285c9/framework/src/play-json/src/main/scala/play/api/libs/json/Format.scala#L13 – Ionuț G. Stan Feb 23 '15 at 16:55
  • Can you please split the Reads one for me below your code, I just want to see it separated out also to learn something. I'm kind of stuck doing that thanks! – Blankman Feb 23 '15 at 17:04
  • @Blankman I've edited my answer. Not sure why you found this difficult, it's a very mechanical process. Maybe there's some concept you're not grasping yet? – Ionuț G. Stan Feb 23 '15 at 17:15
  • Thanks. Yes, I am just used to writing Reads using JsPath \ "abc". I guess I don't understand what exactly the ```JsPath \ "..." and JsPath and ``` then calling .apply is doing. It is good this problem exposes my ignorance :) What also confused me is previously all my Reads I could picture the json being parsed using JsPath, but this time I couldn't picture the Map. – Blankman Feb 23 '15 at 17:21
  • Anyone knows how to do it with case classes? – bashan May 03 '18 at 18:58