Jay Taylor
Jay Taylor

Reputation: 13561

How do you serialize a Map to JSON in Scala?

So I have a Map in Scala like this:

val m = Map[String, String](
    "a" -> "theA",
    "b" -> "theB",
    "c" -> "theC",
    "d" -> "theD",
    "e" -> "theE"
)

and I want to serialize this structure into a JSON string using lift-json.

Do any of you know how to do this?

Upvotes: 29

Views: 48990

Answers (7)

Junji Shimagaki
Junji Shimagaki

Reputation: 316

Supplementing the answer by @Raja.

For those nested object, I locally modify the class to have my wanted toString() like this way:

case class MList[T]() extends MutableList[T] { override def toString() = "[" + this.toList.mkString(",") + "]" }

Then inside the Map object, I use this MList instead of the standard List. That way, my map object prints out fine by calling JSONObject(map).toString().

Upvotes: 0

robert
robert

Reputation: 1532

This code will convert many different objects, and doesn't require any libraries beyond the built-in the scala.util.parsing.json._. It won't properly handle edge cases like Maps with integers as keys.

import scala.util.parsing.json.{JSONArray, JSONObject}
def toJson(arr: List[Any]): JSONArray = {
  JSONArray(arr.map {
    case (innerMap: Map[String, Any]) => toJson(innerMap)
    case (innerArray: List[Any])      => toJson(innerArray)
    case (other)                      => other
  })
}
def toJson(map: Map[String, Any]): JSONObject = {
  JSONObject(map.map {
    case (key, innerMap: Map[String, Any]) =>
      (key, toJson(innerMap))
    case (key, innerArray: List[Any]) =>
      (key, toJson(innerArray))
    case (key, other) =>
      (key, other)
  })
}

Upvotes: 2

Haimei
Haimei

Reputation: 13015

You can use this simple way if you are using play framework:

import play.api.libs.json._

Json.toJson(<your_map>)

Upvotes: 6

jar
jar

Reputation: 391

You can roll your own pretty easily (yay, no dependencies). This one does basic handling of types and will do recursion unlike JSONObject that was mentioned:

import scala.collection.mutable.ListBuffer

object JsonConverter {
  def toJson(o: Any) : String = {
    var json = new ListBuffer[String]()
    o match {
      case m: Map[_,_] => {
        for ( (k,v) <- m ) {
          var key = escape(k.asInstanceOf[String])
          v match {
            case a: Map[_,_] => json += "\"" + key + "\":" + toJson(a)
            case a: List[_] => json += "\"" + key + "\":" + toJson(a)
            case a: Int => json += "\"" + key + "\":" + a
            case a: Boolean => json += "\"" + key + "\":" + a
            case a: String => json += "\"" + key + "\":\"" + escape(a) + "\""
            case _ => ;
          }
        }
      }
      case m: List[_] => {
        var list = new ListBuffer[String]()
        for ( el <- m ) {
          el match {
            case a: Map[_,_] => list += toJson(a)
            case a: List[_] => list += toJson(a)
            case a: Int => list += a.toString()
            case a: Boolean => list += a.toString()
            case a: String => list += "\"" + escape(a) + "\""
            case _ => ;
          }
        }
        return "[" + list.mkString(",") + "]"
      }
      case _ => ;
    }
    return "{" + json.mkString(",") + "}"
  }

  private def escape(s: String) : String = {
    return s.replaceAll("\"" , "\\\\\"");
  }
}

You can see it in action like

println(JsonConverter.toJson(
    Map("a"-> 1,
        "b" -> Map(
            "nes\"ted" -> "yeah{\"some\":true"),
            "c" -> List(
                1,
                2,
                "3",
                List(
                    true,
                    false,
                    true,
                    Map(
                        "1"->"two",
                        "3"->"four"
                    )
                )
            )
        )
    )
)

{"a":1,"b":{"nes\"ted":"yeah{\"some\":true"},"c":[1,2,"3",[true,false,true,{"1":"two","3":"four"}]]}

(It's part of a Coinbase GDAX library I've written, see util.scala)

Upvotes: 10

jwhitlark
jwhitlark

Reputation: 505

Similar to Einar's solution, you can use JSONObject from Parser Combinators to do this. Note that it does not recurse, you'll need to do this yourself. The library also includes JSONArray, for list like data structures. Something like the following will address Noel's concerns about nested structures. This example does not recurse to an arbitrary level, but will handle a value of List[Map[String, Any]].

import scala.util.parsing.json.{JSONArray, JSONObject}

def toJson(m : Map[String, Any]): String = JSONObject(
  m.mapValues {
    case mp: Map[String, Any] => JSONObject(mp)
    case lm: List[Map[String, Any]] => JSONArray(lm.map(JSONObject(_)))
    case x => x
    }
  ).toString

Upvotes: 1

user788234
user788234

Reputation:

How about this?

implicit val formats = net.liftweb.json.DefaultFormats
import net.liftweb.json.JsonAST._
import net.liftweb.json.Extraction._
import net.liftweb.json.Printer._
val m = Map[String, String](
    "a" -> "theA",
    "b" -> "theB",
    "c" -> "theC",
    "d" -> "theD",
    "e" -> "theE"
)
println(compact(render(decompose(m))))

output:

{"e":"theE","a":"theA","b":"theB","c":"theC","d":"theD"}

EDIT:

For a scala.collections.mutable.Map, you should convert it first to an immutable map: .toMap

Upvotes: 28

Raja
Raja

Reputation: 403

If you are using the latest Scala 2.10.x and above :

println(scala.util.parsing.json.JSONObject(m))

Upvotes: 37

Related Questions