web-dev-qa-db-fra.com

Comment sérialiser une carte vers JSON dans Scala?

J'ai donc une carte en Scala comme celle-ci:

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

et je veux sérialiser cette structure dans une chaîne JSON en utilisant lift-json.

Est-ce que l'un de vous sait comment faire cela?

20
Jay Taylor

Que dis-tu de ça?

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

sortie:

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

MODIFIER:

Pour un scala.collections.mutable.Map, vous devez d'abord le convertir en une carte immuable: .toMap

26
user788234

Si vous utilisez les dernières versions de Scala 2.10.x et supérieures:

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

Vous pouvez rouler le vôtre assez facilement (oui, pas de dépendances). Celui-ci gère les types de base et fera la récursivité contrairement à JSONObject qui a été mentionné:

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("\"" , "\\\\\"");
  }
}

Vous pouvez le voir en action comme

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"}]]}

(Il fait partie d'une bibliothèque Coinbase GDAX que j'ai écrite, voir util.scala )

5
jar

Vous pouvez utiliser ce moyen simple si vous utilisez un cadre de jeu:

import play.api.libs.json._

Json.toJson(<your_map>)
3
Haimei

Ce code convertira de nombreux objets différents et ne nécessite aucune bibliothèque au-delà du scala.util.parsing.json._ intégré. Il ne gérera pas correctement les cas Edge tels que les cartes avec des nombres entiers comme clés.

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)
  })
}
1
robert

Semblable à la solution d'Einar, vous pouvez utiliser JSONObject à partir de Combinators Parser pour le faire. Notez qu'il ne recurse pas, vous devrez le faire vous-même. La bibliothèque comprend également JSONArray, une liste comme les structures de données. Quelque chose comme ce qui suit va répondre aux préoccupations de Noel concernant les structures imbriquées. Cet exemple ne renvoie pas à un niveau arbitraire, mais gérera une valeur de 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
0
jwhitlark

Complétant la réponse de @Raja.

Pour ces objets imbriqués, je modifie localement la classe pour que ma toString() souhaitée se présente comme suit:

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

Ensuite, à l'intérieur de l'objet Map, j'utilise cette MList au lieu de la List standard. De cette façon, mon objet map s'imprime correctement en appelant JSONObject(map).toString().

0
Junji Shimagaki