无法将 jsonlite::stream_in 用于某些 JSON 格式

Unable to use jsonlite::stream_in with certain JSON formats

我正在尝试从 YASP 数据转储 (https://github.com/yasp-dota/yasp/wiki/JSON-Data-Dump) 流式传输相当大的 (65gb) JSON 文件,但似乎 JSON 文件的方式是格式化意味着我无法读取文件,并给出此错误:

Error: parse error: premature EOF [ (right here) ------^

我使用相同的格式创建了这个小示例 JSON 文件,以便其他人可以轻松地重新创建它:

[
{"match_id": 2000594819,"match_seq_num": 1764515493}
,
{"match_id": 2000594820,"match_seq_num": 1764515494}
,
{"match_id": 2000594821,"match_seq_num": 1764515495}
]

我已将此文件保存为 test.json,并尝试通过 jsonlite::stream_in 函数加载它

library(jsonlite)
con <- file('~/yasp/test.json')
jsonStream <- stream_in(con)

我得到与上图相同的 "premature EOF" 错误。

但是,如果文件的格式全部在一个单独的块中,如下所示:

[{"match_id": 2000594819,"match_seq_num": 1764515493},{"match_id": 2000594820,"match_seq_num": 1764515494},{"match_id": 2000594821,"match_seq_num": 1764515495}]

那么就没有问题了,stream_in 工作正常。

我尝试过使用 readLines,并在读取之前折叠框架:

initialJSON <- readLines('~/yasp/test.json')
collapsedJSON <- paste(initialJSON, collapse="")

虽然这确实有效并创建了一个我可以从 JSON 读取的字符串,但这对我来说不是一个可扩展的解决方案,因为我一次只能读取几千行,并且可扩展性不是很好(我也希望能够直接从 gz 文件流式传输)。

有谁知道如何让 stream_in 接受这种文件格式,或者使用 R 的其他替代方法?他们展示了它如何工作的示例 Java 很好,但我希望能够做到这一点而无需跳入我并不真正了解的语言。

更新

仍然没有让流工作,但写了我自己的(某种程度上),似乎对我的目的表现不错。

fileCon <- file('~/yasp/test.json', open="r")

# Initialize everything
numMatches <- 5
outputFile <- 0
lineCount <- 0
matchCount <- 0
matchIDList <- NULL

# Stream using readLines and only look at even numbered lines
while(matchCount <= numMatches) {
    next_line = readLines(fileCon, n = 1)

    lineCount <- lineCount + 1

    if(lineCount %% 2 == 0) {

        matchCount <- matchCount + 1

        # read into JSON format
        readJSON <- jsonlite::fromJSON(next_line)

        # Up the match counter
        matchCount <- matchCount + 1

        # whatever operations you want, for example get match_id
        matchIDList <- c(matchIDList, readJSON$match_id)
    }

}

好吧,我从来没有使用过 stream_in 功能,但我创建了自己的流媒体,它运行良好且占用空间小。

streamJSON <- function(con, pagesize, numMatches){
  library(jsonlite)
  library(data.table)
  library(plyr)
  library(dplyr)
  library(tidyr)

  ## "con" is the file connection
  ## "pagesize" is number of lines streamed for each iteration.
  ## "numMatches" is number of games we want to output

  outputFile <- 0
  matchCount <- 0
  print("Starting parsing games...")
  print(paste("Number of games parsed:",matchCount))
  # Stream in using readLines until we reach the number of matches we want.
  while(matchCount < numMatches) {

    initialJSON = readLines(con, n = pagesize)

    collapsedJSON <- paste(initialJSON[2:pagesize], collapse="")
    fixedJSON <- sprintf("[%s]", collapsedJSON, collapse=",")
    readJSON <- jsonlite::fromJSON(fixedJSON)

    finalList <- 0
    ## Run throught he initial file
    for (i in 1:length(readJSON$match_id)) {
      ## Some work with the JSON to return whatever it is i wanted to return
      ## In this example match_id, who won, and the duration.

      matchList <- as.data.frame(cbind(readJSON$match_id[[i]],
                                    readJSON$radiant_win[[i]],
                                    readJSON$duration[[i]]))
      colnames(matchList) <- c("match_id", "radiant_win", "duration")

      ## Assign to output
      if (length(finalList) == 1) {
        finalList <- matchList
      } else {
        finalList <- rbind.fill(finalList, matchList)
      } 
    }

    matchCount <- matchCount + length(unique(finalList[,1]))

    if (length(outputFile) == 1) {
       outputFile <- finalList
    } else {
      outputFile <- rbind.fill(outputFile, finalList)
    } 
    print(paste("Number of games parsed:",matchCount))
  }
  return(outputFile)
}

不确定这是否对其他人有帮助,因为它可能有点特定于 YASP 数据转储,但我现在可以这样调用此函数:

fileCon <- gzfile('~/yasp/yasp-dump-2015-12-18.json.gz', open="rb")
streamJSONPos(fileCon, 100, 500)

这将输出一个包含指定数据的 500 行数据帧,然后我必须修改 while 循环中的部分,无论我想从 JSON 数据中提取什么。

我已经能够流式传输 50.000 场比赛(使用相当复杂的 JSON 函数),非常容易,并且似乎 运行 在可比的时间(每场比赛)作为 stream_in 函数是。