如何将Scala Map转换为JSON字符串?

27

例如,我在Scala中有这个Map值:

val m = Map(
    "name" -> "john doe", 
    "age" -> 18, 
    "hasChild" -> true, 
    "childs" -> List(
        Map("name" -> "dorothy", "age" -> 5, "hasChild" -> false),
        Map("name" -> "bill", "age" -> 8, "hasChild" -> false)
    )
)
{"我想将其转换为JSON字符串表示形式:"}
{
    "name": "john doe",
    "age": 18,
    "hasChild": true,
    "childs": [
        {
            "name": "dorothy",
            "age": 5,
            "hasChild": false
        },
        {
            "name": "bill",
            "age": 8,
            "hasChild": false
        }
    ]
}

我目前正在使用Play框架v2.3进行开发,但解决方案不需要使用Play JSON库,如果有人能提供Play和非Play的解决方案会很好。

这是我到目前为止没有成功的尝试:

// using jackson library
val mapper = new ObjectMapper()
val res = mapper.writeValueAsString(m)
println(res)

结果:

{"empty":false,"traversableAgain":true}

我不明白为什么我得到了那个结果。

8个回答

29
作为一种非玩耍解决方案,您可以考虑使用 json4s ,它提供了一个包装器来包装 jackson 并且易于使用。 如果您正在使用 json4s,则可以通过以下方式将 map 转换为 json:
write(m)                                        
//> res0: String = {"name":"john doe","age":18,"hasChild":true,"childs":[{"name":"dorothy","age":5,"hasChild":false},{"name":"bill","age":8,"hasChild":false}]}

--更新以包含完整示例--

import org.json4s._
import org.json4s.native.Serialization._
import org.json4s.native.Serialization
implicit val formats = Serialization.formats(NoTypeHints)

 val m = Map(
  "name" -> "john doe",
  "age" -> 18,
  "hasChild" -> true,
  "childs" -> List(
    Map("name" -> "dorothy", "age" -> 5, "hasChild" -> false),
    Map("name" -> "bill", "age" -> 8, "hasChild" -> false)))

 write(m)

输出:

 res0: String = {"name":"john doe","age":18,"hasChild":true,"childs":[{"name" 
 :"dorothy","age":5,"hasChild":false},{"name":"bill","age":8,"hasChild":false }]}

另一种方式:

import org.json4s.native.Json
import org.json4s.DefaultFormats

Json(DefaultFormats).write(m)

嗨,Mohit,能否给出完整的代码示例?如果我只写 write(m),那么会得到 not found 的编译错误。 - null
@suud - 已更新。在sbt中添加json4s依赖项以支持jackson后,它将正常工作。 - mohit
你的代码在我尝试时没有工作,因为我使用了来自Json4s的Lift Json版本,导入方式不同。我已经知道如何解决它,但我会给你一个机会来回答 :) - null
@suud - 我已经更改了导入。我猜想不会再有更改了。无论如何,如果有任何更改,为什么不继续编辑答案,这样将对未来寻找类似内容的其他人有所帮助 :) - mohit
你好,你的解决方案与我的有点不同,尽管两者都可以正常工作。我只是将我的解决方案添加到了你的答案中。你知道你的解决方案和我的有什么区别吗?我看到你在使用隐式的 NoTypeHints,而我在使用 DefaultFormats - null
1
@suud 我不是完全确定。两者都告诉json4s如何序列化/反序列化。当我想让json4s处理所有内容并使用默认格式时,我不使用类型提示;当我想要覆盖某些字段(如日期时间格式)时,我使用defaultFormats。 - mohit

20
val mapper = new ObjectMapper()
mapper.writeValueAsString(Map("a" -> 1))
< p >结果> {"empty":false,"traversableAgain":true}

==============================

import com.fasterxml.jackson.module.scala.DefaultScalaModule

val mapper = new ObjectMapper()
mapper.registerModule(DefaultScalaModule)
mapper.writeValueAsString(Map("a" -> 1))

结果> {"a":1}


2
虽然这段代码可能回答了问题,但提供有关它如何以及/或为什么解决问题的附加上下文将改善答案的长期价值。 - thewaywewere
你应该创建一个新对象。例如,mapper.registerModule(new DefaultScalaModule())。 - saumilsdk

9

您需要告诉Jackson如何处理Scala对象: mapper.registerModule(DefaultScalaModule)


我遇到了编译错误:找不到“DefaultScalaModule”。我在谷歌上搜索后发现我需要添加“import com.fasterxml.jackson.module.scala.DefaultScalaModule”,但是该类未被找到。 - null
你可以从我通常的地方下载它。比如这里 - Dima
1
libraryDependencies += "com.fasterxml.jackson.module" % "jackson-module-scala" % "2.4.4" - Dima
我也遇到了同样的错误。解决方法是使用mapper.registerModule(new DefaultScalaModule())。 - saumilsdk

7
val mymap = array.map {
  case 1 => ("A", 1)
  case 2 => ("B", 2)
  case 3 => ("C", 3)
}
  .toMap

使用 scala.util.parsing.json.JSONObject只需要一行代码:
import scala.util.parsing.json.JSONObject

JSONObject(mymap).toString()

2
注意,它不会递归,并且会将嵌套对象转换为它们的字符串表示形式! - seb
2
另外,它已被弃用(使用Scala 2.11.12)。我现在没有做需要担心这个问题的事情,但我想提一下。 - DataMacGyver

2
如果你正在使用一个明确定义的数据模型,为什么不定义case类并使用Play JSON宏来处理转换呢?例如:
case class Person(name: String, age: Int, hasChild: Boolean, childs: List[Person])

implicit val fmt = Json.format[Person]

val person = Person(...)

val jsonStr = Json.toJson(person)

嗨,谢谢提供信息。在我的情况下,没有涉及到POJO(或者也许是POSO)。 - null

1
使用Jackson库的一种方法是使用Java的HashMap对象,而不是Scala对象。然后,您基本上可以使用您已经编写的相同的“失败”代码。
import org.codehaus.jackson.map.ObjectMapper
val mapper = new ObjectMapper()
val jmap = new java.util.HashMap[String, Int]()
jmap.put("dog", 4)
jmap.put("cat", 1)
// convert to json formatted string
val jstring  = mapper.writeValueAsString(jmap)
println(jstring)

返回
jstring: String = {"dog":4,"cat":1}    

在Scala的良好实践方面,使用Java类在Scala代码中是否可以? - null
1
在Scala中使用Java对象时,我建议不要在代码顶部导入,而是像这个例子一样实例化Java对象:“val jmap = new java.util.HashMap(...)”等。这样你的命名空间就不会在Java和Scala类型之间混淆。 - bill_e

0

如果有人正在寻找使用标准库的解决方案。

def toJson(query: Any): String = query match {
  case m: Map[String, Any] => s"{${m.map(toJson(_)).mkString(",")}}"
  case t: (String, Any) => s""""${t._1}":${toJson(t._2)}"""
  case ss: Seq[Any] => s"""[${ss.map(toJson(_)).mkString(",")}]"""
  case s: String => s""""$s""""
  case null => "null"
  case _ => query.toString
}

1
当一个字符串包含反斜杠或文字引号时会发生什么? - LexH

-1
以下摘自官方Scala网站的代码片段对我非常有用。 它使用uPickle库。
val map: Map[String, Int] =
  Map("Toolkitty" -> 3, "Scaniel" -> 5)
val jsonString: String = upickle.default.write(map)
println(jsonString)
// prints: {"Toolkitty":3,"Scaniel":5}

https://docs.scala-lang.org/toolkit/json-serialize.html


虽然这个链接可能回答了问题,但最好在这里包含答案的关键部分,并提供链接作为参考。仅有链接的答案如果链接页面发生变化,就会失效。- 来自评论 - XMehdi01
谢谢 @XMehdi01。我添加了代码片段。 - Tharaka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接