无法将jsonlite :: stream_in与某些JSON格式一起使用

时间:2016-05-04 21:12:26

标签: json r jsonlite

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

  

错误:解析错误:过早的EOF                                         [                       (就在这里)------ ^

我使用相同的格式创建了这个小样本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)

我得到了相同的“过早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读取的字符串,但这对我来说不是一个可扩展的解决方案,因为我一次只能读取几千行,并且不是很可扩展(i 'd也喜欢能够直接从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)
    }

}

1 个答案:

答案 0 :(得分:0)

嗯,我从来没有让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函数相同的时间(每次匹配)运行。