18

How do I serialize Map[String, Any] with spray-json? I try

val data = Map("name" -> "John", "age" -> 42)
import spray.json._
import DefaultJsonProtocol._
data.toJson

It says Cannot find JsonWriter or JsonFormat type class for scala.collection.immutable.Map[String,Any].

Yaroslav
  • 3,897
  • 4
  • 21
  • 32

2 Answers2

28

Here's an implicit converter I used to do this task:

  implicit object AnyJsonFormat extends JsonFormat[Any] {
    def write(x: Any) = x match {
      case n: Int => JsNumber(n)
      case s: String => JsString(s)
      case b: Boolean if b == true => JsTrue
      case b: Boolean if b == false => JsFalse
    }
    def read(value: JsValue) = value match {
      case JsNumber(n) => n.intValue()
      case JsString(s) => s
      case JsTrue => true
      case JsFalse => false
    }
  }

It was adapted from this post in the Spray user group, but I couldn't get and didn't need to write nested Sequences and Maps to Json so I took them out.

Gangstead
  • 3,907
  • 18
  • 33
  • 2
    Awesome, it works for me. I just need to make sure I declare this object BEFORE the other JsonFormat implicits that depend on it – Bogdan Feb 06 '15 at 16:58
  • 1
    And hot to use it? I copypasted this code above my immutableMap.toJson. But still getting "Can not find Json writer..." error. – Sergey Yarotskiy Jun 26 '15 at 15:35
  • Sorry, Made it work. Looks like I still used mutable map, and it must be immutable in order to this examlpe works. – Sergey Yarotskiy Jun 26 '15 at 15:48
  • 1
    Thanks. You could simply use 'case true => JsTrue' and 'case false => JsFalse', I think. Simpler - no functional change. – akauppi Jun 30 '15 at 07:48
  • I'm curious why it is not a part of spray-json – Soid Sep 23 '16 at 21:39
  • I used the code provided in the [post referenced](https://groups.google.com/d/msg/spray-user/zZl_LbH8fN8/ITDQy3PkbQsJ) with spray.json 1.3.3 and it works fine with nested elements as well. I guess that this is not included in spray-json because it needs to identify the types of the elements in runtime instead of compilation time... – aitorhh Oct 26 '17 at 16:09
  • I cannot understand how this works. The input to write is of type `Any`. While it should be of type `Map[String,Any]`. I tried to add this code in my implementation that only requires a `Map[String,String]` and it obviously does not work. – Niko Nov 24 '20 at 10:39
7

Another option, which should work in your case, is

import spray.json._
import DefaultJsonProtocol._

data.parseJson.convertTo[Map[String, JsValue]]
Raman Mishra
  • 2,392
  • 2
  • 10
  • 30
  • I like that you mentioned this, because in many cases using `JsValue` for the values might be enough for people. It's also more in line with spray.json mentality, imho, than handling Any's. – akauppi Feb 11 '16 at 08:12
  • 6
    `data` is of type `Map[String,Any]` - what should I import to make it have `parseJson` method? – mirelon Jul 27 '16 at 14:24
  • I believe what he meant was data.toJson – binshi May 10 '18 at 11:17
  • 2
    @mirelon probably a way to dated comment - but for others reading this: you should ensure to `import spray.json._` and `import DefaultJsonProtocol._` – pellekrogholt Sep 03 '18 at 12:29
  • spent the whole day on this. Solution works like a charm. ...Finally time to go to bed. Thank you very much. – shalama Dec 04 '20 at 19:52