【问题标题】:How do you serialize a Map to JSON in Scala?如何在 Scala 中将 Map 序列化为 JSON?
【发布时间】:2011-09-10 09:54:36
【问题描述】:

所以我在 Scala 中有一个这样的地图:

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

我想使用 lift-json 将此结构序列化为 JSON 字符串。

你们中有人知道怎么做吗?

【问题讨论】:

    标签: json scala serialization map lift


    【解决方案1】:

    如果您使用的是最新的 Scala 2.10.x 及更高版本:

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

    【讨论】:

    • 它已从 Scala 2.11 及更高版本中删除。
    • 我可能在某处犯了错误。但是你能用吗?您使用了哪个版本的 scala-parser-combinators(完整的 maven 依赖项会有所帮助)?
    • 看起来它没有正确递归到对象中。例如,Map("0" -> 1, "1" -> List(Map("2" -> 3))) 被字符串化为 {"0" : 1, "1" : List(Map(2 -> 3))}
    • 注意,它不会递归,并且会将嵌套对象转换为它们的字符串表示形式!
    【解决方案2】:

    这个怎么样?

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

    输出:

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

    编辑:

    对于scala.collections.mutable.Map,您应该先将其转换为不可变映射:.toMap

    【讨论】:

    • 当我将类型更改为 scala.collections.mutable.Map 时,输出变为:[{"_1":"d","_2":"theD"},{"_1": "a","_2":"theA"},{"_1":"c","_2":"theC"},{"_1":"b","_2":"theB"},{ "_1":"e","_2":"theE"}],这让我有点难过。
    • 有一个简单的解决方法 - 只需在可变映射上调用 .toMap 以获取不可变映射,然后一切正常。
    【解决方案3】:

    您可以很容易地推出自己的产品(是的,没有依赖关系)。与提到的JSONObject 不同,这个对类型进行基本处理并将进行递归:

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

    你可以看到它的实际效果

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

    (它是我编写的 Coinbase GDAX 库的一部分,请参阅 util.scala

    【讨论】:

      【解决方案4】:

      如果你使用 play framework,你可以使用这个简单的方法:

      import play.api.libs.json._
      
      Json.toJson(<your_map>)
      

      【讨论】:

        【解决方案5】:

        此代码将转换许多不同的对象,并且不需要内置 scala.util.parsing.json._ 之外的任何库。它不能正确处理像使用整数作为键的 Maps 这样的边缘情况。

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

        【讨论】:

          【解决方案6】:

          与 Einar 的解决方案类似,您可以使用来自 Parser Combinators 的 JSONObject 来执行此操作。请注意,它不会递归,您需要自己执行此操作。该库还包括 JSONArray,用于类似数据结构的列表。以下内容将解决 Noel 对嵌套结构的担忧。此示例不会递归到任意级别,但会处理 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
          

          【讨论】:

            【解决方案7】:

            补充@Raja 的答案。

            对于那些嵌套对象,我在本地修改类以拥有我想要的toString(),如下所示:

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

            然后在 Map 对象中,我使用这个MList 而不是标准的List。这样,我的map 对象通过调用JSONObject(map).toString() 可以很好地打印出来。

            【讨论】:

              猜你喜欢
              • 2016-08-03
              • 2021-10-02
              • 1970-01-01
              • 2013-07-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多