关于在Scala中将映射编写为JSON文件的建议

关于在Scala中将映射编写为JSON文件的建议,json,scala,map,Json,Scala,Map,我有一个简单的单键值映射(K,V)myDictionary,它由我的程序填充,最后我想将它作为JSON格式的字符串写入一个文本文件,因为我以后需要解析它们 我之前用过这个代码 Some(new PrintWriter(outputDir+"/myDictionary.json")).foreach{p => p.write(compact(render(decompose(myDictionary)))); p.close} 我发现它会随着输入大小的增加而变慢。后来,我使用了这个var

我有一个简单的单键值映射(K,V)myDictionary,它由我的程序填充,最后我想将它作为JSON格式的字符串写入一个文本文件,因为我以后需要解析它们

我之前用过这个代码

Some(new PrintWriter(outputDir+"/myDictionary.json")).foreach{p => p.write(compact(render(decompose(myDictionary)))); p.close}
我发现它会随着输入大小的增加而变慢。后来,我使用了这个var out=new

var out = new PrintWriter(outputDir+"/myDictionary.json");
out.println(scala.util.parsing.json.JSONObject(myDictionary.toMap).toString())
事实证明,这要快一点


我已经为示例输入运行了这个程序,发现它比我以前的方法更快。我假设我的输入映射大小将至少达到一百万个值(>1GB文本文件)(K,V),因此我想确保我遵循更快、内存效率更高的映射序列化方法。您会推荐哪些其他方法,我可以研究以优化此过程

标准Scala库中的JSON支持可能不是最佳选择。不幸的是,Scala的JSON库的情况有点混乱,有(Lift JSON、Play JSON、Spray JSON、Twitter JSON、Argonaut等),基本上一周中每天都有一个库。。。我建议你至少看看这些,看看它们是否更易于使用,更具表现力


下面是一个使用Play JSON的示例,我之所以选择它是出于特殊原因(能够使用宏生成格式):

虽然可以直接构造和解构
JsValues
,但主要思想是使用
格式[a]
,其中
a
是数据结构的类型。这比标准的Scala库JSON更强调类型安全。它看起来更冗长,但最终我认为这是更好的方法

有实用程序方法
Json.toJson
Json.fromJson
可以查找所需类型的隐式格式

另一方面,它确实在内存中构建了所有内容,并复制了您的数据结构(因为对于地图中的每个条目,您都会有另一个元组
(String,JsValue)
),因此,考虑到您在GB量级中操作,这不一定是最节省内存的解决方案



Jerkson是javajson库的Scala包装器。后者。我发现它增加了流媒体支持。反过来,Play JSON是基于Jerkson的,所以您甚至可以知道如何使用它来流式处理您的对象。另请参见。

标准Scala库中的JSON支持可能不是最佳选择。不幸的是,Scala的JSON库的情况有点混乱,有(Lift JSON、Play JSON、Spray JSON、Twitter JSON、Argonaut等),基本上一周中每天都有一个库。。。我建议你至少看看这些,看看它们是否更易于使用,更具表现力


下面是一个使用Play JSON的示例,我之所以选择它是出于特殊原因(能够使用宏生成格式):

虽然可以直接构造和解构
JsValues
,但主要思想是使用
格式[a]
,其中
a
是数据结构的类型。这比标准的Scala库JSON更强调类型安全。它看起来更冗长,但最终我认为这是更好的方法

有实用程序方法
Json.toJson
Json.fromJson
可以查找所需类型的隐式格式

另一方面,它确实在内存中构建了所有内容,并复制了您的数据结构(因为对于地图中的每个条目,您都会有另一个元组
(String,JsValue)
),因此,考虑到您在GB量级中操作,这不一定是最节省内存的解决方案



Jerkson是javajson库的Scala包装器。后者。我发现它增加了流媒体支持。反过来,Play JSON是基于Jerkson的,所以您甚至可以知道如何使用它来流式处理您的对象。另请参见。

正是我的问题。选择太多了,除了这篇文章,我找不到比较它们的合适文章。谷歌搜索也帮不了我。我添加了一个播放JSON的例子。我隐约记得,其中一个库也可能使用流式I/O,这可能会避免使用两倍于输入数据结构的内存…如果内存是一个问题,也许你可以把你的大地图分割成几个卡盘,这些卡盘被编码成单独的JSON对象……谢谢你的详细更新。记忆不是问题。正是这样,将其作为文件写入并读回需要很长时间。有没有办法减少开销?没有,性能最多只能与地图的大小成线性关系。如果它阻止了您的应用程序,请在专用线程或将来执行I/O。。。否则,您可以使用不同的机制,比如键值存储(我对BerkeleyDB JE有很好的经验)。还有,不知道它有多成熟。这正是我的问题。选择太多了,除了这篇文章,我找不到比较它们的合适文章。谷歌搜索也帮不了我。我添加了一个播放JSON的例子。我隐约记得,其中一个库也可能使用流式I/O,这可能会避免使用两倍于输入数据结构的内存…如果内存是一个问题,也许你可以把你的大地图分割成几个卡盘,这些卡盘被编码成单独的JSON对象……谢谢你的详细更新。记忆不是问题。正是这样,将其作为文件写入并读回需要很长时间。有没有办法减少开销?没有,性能最多只能与地图的大小成线性关系。如果它阻止了您的应用程序,请在专用线程或将来执行I/O。。。否则,您可以使用不同的机制,比如键值存储(我对BerkeleyDB JE有很好的经验)。还有,不知道它有多成熟。
object JsonTest extends App {
  import play.api.libs.json._

  type MyDict = Map[String, Int]

  implicit object MyDictFormat extends Format[MyDict] {
    def reads(json: JsValue): JsResult[MyDict] = json match {
      case JsObject(fields) =>
        val b = Map.newBuilder[String, Int]
        fields.foreach {
          case (k, JsNumber(v)) => b += k -> v.toInt
          case other => return JsError(s"Not a (string, number) pair: $other")
        }
        JsSuccess(b.result())

      case _ => JsError(s"Not an object: $json")
    }

    def writes(m: MyDict): JsValue = {
      val fields: Seq[(String, JsValue)] = m.map {
        case (k, v) => k -> JsNumber(v)
      } (collection.breakOut)

      JsObject(fields)
    }
  }

  val m       = Map("hallo" -> 12, "gallo" -> 34)
  val serial  = Json.toJson(m)
  val text    = Json.stringify(serial)
  println(text)
  val back    = Json.fromJson[MyDict](serial)
  assert(back == JsSuccess(m), s"Failed: $back")
}