扫描一个巨大的JSON文件,用于Scala中的反序列化数据
我需要能够处理大型JSON文件,在文件中迭代/流式传输时,从反序列化的子字符串实例化对象。
例如:
假设我只能反序列化为以下情况:
case class Data(val a: Int, val b: Int, val c: Int)
预期的JSON格式是:
{ "foo": [ {"a": 0, "b": 0, "c": 0 }, {"a": 0, "b": 0, "c": 1 } ],
"bar": [ {"a": 1, "b": 0, "c": 0 }, {"a": 1, "b": 0, "c": 1 } ],
.... MANY ITEMS .... ,
"qux": [ {"a": 0, "b": 0, "c": 0 } }
我想要做的是:
import com.codahale.jerkson.Json
val dataSeq : Seq[Data] = Json.advanceToValue("foo").stream[Data](fileStream)
// NOTE: this will not compile since I pulled the "advanceToValue" out of thin air.
作为最后一点,我希望找到一个涉及Jerkson或Play框架附带的其他库的解决方案,但是如果另一个Scala库以更轻松和体面的性能处理这种情况:我不反对尝试另一个库。 如果有一种干净的方式来手动搜索文件,然后使用Json库继续从那里解析:我很好。
我不想做的是摄取整个文件而无需流式处理或使用迭代器,因为一次只将整个文件保存在内存中将会非常昂贵。
我没有用JSON来完成(我希望有人会为你提供一个交钥匙解决方案),但是用XML来完成它,并且这是一种处理它的方法。
在流解析器的帮助下,它基本上是一个简单的Map-> Reduce过程。
地图 (你的advanceTo
)
使用流式解析器,如JSON Simple(未测试)。 回调时,您匹配您的“路径”,通过将其写入流(文件支持或内存中,取决于您的数据)来收集以下任何内容。 这将是你的例子中的foo
数组。 如果您的映射器足够复杂,则可能需要在映射步骤中收集多个路径。
减少 (您的stream[Data]
)
由于上面收集的流看起来很小,因此您可能不需要再次映射/拆分它们,并且可以将它们直接在内存中解析为JSON对象/数组并对它们进行操作(变换,重新组合等)。
以下是我现在解决问题的方式:
import collection.immutable.PagedSeq
import util.parsing.input.PagedSeqReader
import com.codahale.jerkson.Json
import collection.mutable
private def fileContent = new PagedSeqReader(PagedSeq.fromFile("/home/me/data.json"))
private val clearAndStop = ']'
private def takeUntil(readerInitial: PagedSeqReader, text: String) : Taken = {
val str = new StringBuilder()
var readerFinal = readerInitial
while(!readerFinal.atEnd && !str.endsWith(text)) {
str += readerFinal.first
readerFinal = readerFinal.rest
}
if (!str.endsWith(text) || str.contains(clearAndStop))
Taken(readerFinal, None)
else
Taken(readerFinal, Some(str.toString))
}
private def takeUntil(readerInitial: PagedSeqReader, chars: Char*) : Taken = {
var taken = Taken(readerInitial, None)
chars.foreach(ch => taken = takeUntil(taken.reader, ch.toString))
taken
}
def getJsonData() : Seq[Data] = {
var data = mutable.ListBuffer[Data]()
var taken = takeUntil(fileContent, ""foo"")
taken = takeUntil(taken.reader, ':', '[')
var doneFirst = false
while(taken.text != None) {
if (!doneFirst)
doneFirst = true
else
taken = takeUntil(taken.reader, ',')
taken = takeUntil(taken.reader, '}')
if (taken.text != None) {
print(taken.text.get)
places += Json.parse[Data](taken.text.get)
}
}
data
}
case class Taken(reader: PagedSeqReader, text: Option[String])
case class Data(val a: Int, val b: Int, val c: Int)
当然,这段代码并不完全处理格式错误的JSON,并且要用于多个顶级密钥“foo”,“bar”和“qux”,将需要向前看(或从可能的顶级密钥列表中进行匹配),但总的来说:我相信这是做这项工作的。 它不像我想要的那样功能强大,并且不是超级健壮的,但PagedSeqReader绝对可以避免这种情况变得过于混乱。
链接地址: http://www.djcxy.com/p/11737.html上一篇: Scanning a HUGE JSON file for deserializable data in Scala