7

大きな 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 ライブラリを使用してそこから解析を続行するクリーンな方法があれば、それで問題ありません。

私がやりたくないのは、一度にファイル全体をメモリに保持すると法外なコストがかかるため、ストリーミングやイテレータを使用せずにファイル全体を取り込むことです。

4

2 に答える 2

2

私は JSON でそれを行ったことはありません (誰かがターンキー ソリューションを考え出すことを願っています) が、XML でそれを行いました。ここにそれを処理する方法があります。

これは基本的に、ストリーム パーサーを使用した単純な Map->Reduce プロセスです。

マップ(あなたのadvanceTo)

JSON Simpleのようなストリーミング パーサーを使用します(テストされていません)。コールバックで「パス」と一致する場合は、ストリームに書き込むことで以下のものを収集します (データに応じて、バックアップされたファイルまたはメモリ内のファイル)。それがfooあなたの例の配列になります。マッパーが十分に洗練されている場合は、マップ ステップで複数のパスを収集することができます。

減らす(あなたのstream[Data])

上記で収集したストリームはかなり小さいように見えるため、おそらく再度マップ/分割する必要はなく、JSON オブジェクト/配列としてメモリ内で直接解析し、操作 (変換、再結合など) できます。

于 2013-01-17T10:19:11.570 に答える
1

これが私が問題を解決している現在の方法です:

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 はこれが乱雑になるのを確実に防ぎます

于 2013-01-17T20:52:22.437 に答える