如何在 Scala 中将 Map 序列化为 JSON?

Posted

技术标签:

【中文标题】如何在 Scala 中将 Map 序列化为 JSON?【英文标题】:How do you serialize a Map to JSON in Scala? 【发布时间】: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 字符串。

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

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

它已从 Scala 2.11 及更高版本中删除。 @Soid 看起来还在那里:scala-lang.org/api/2.11.8/scala-parser-combinators/… 我可能在某处犯了错误。但是你能用吗?您使用了哪个版本的 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() 可以很好地打印出来。

【讨论】:

以上是关于如何在 Scala 中将 Map 序列化为 JSON?的主要内容,如果未能解决你的问题,请参考以下文章

Json序列化与反序列化

如何在 Jackson 中将对象序列化为 ObjectNode 的值?

如何在 Ruby 中将对象序列化为 json

如何在 Jquery 中将 MVC 表单序列化为 JSON

如何在nlog中将属性序列化为json

如何在c#中将xml反序列化为列表