在Ruby中逐对象解析巨大的json对象



问题

我有一个json文件,它由一个巨大的小json对象数组组成。现在,如果我试图用传统的方法解析它,将文件读取到内存中,然后调用它上的任何json解析(例如json.parse或Oj.parse),它将消耗我所有的系统可用内存,无法完成。

我想要什么

以某种方式通过流解析它,每次它完成一个对象时,它都会用该对象回调一个函数。有了这个,我相信内存使用率会非常低,而且是恒定的。

到目前为止我完成了什么

我检查了两个gem(yajl和json流),并使用yajl找到了以下解决方案:

def post_init
  @parser = Yajl::Parser.new(:symbolize_keys => true)
end
def object_parsed(obj)
  puts "Sometimes one pays most for the things one gets for nothing. - Albert Einstein"
  puts obj.inspect
end
def connection_completed
  # once a full JSON object has been parsed from the stream
  # object_parsed will be called, and passed the constructed object
  @parser.on_parse_complete = method(:object_parsed)
end
# Parse itself
post_init
connection_complete
@parse << File.read("data.json",2048)

但这种方法仍然存在一个问题,只有在数组关闭后(即json解析完成后)才会触发@parser.on_parse_complete。但另一方面,如果我用每行一个对象来格式化json,它会很好地工作,函数object_parsed会被调用两次,每行一次。

Json样本:

[
  {
    "v": {
      "M0": 2
    },
    "dims": {
      "D371665580_86": "M77",
      "D2088848381_86": "M5",
      "D372510617_86": "M42"
    }
  },
  {
    "v": {
      "M0": 2
    },
    "dims": {
      "D371665580_86": "M77",
      "D2088848381_86": "M5",
      "D372510617_86": "M42"
    }
  }
]

我会忘记规则,采用以下方法:

#!/usr/bin/env ruby
require 'stringio' # for tests
input = '[{"menu": {
          "id": "file",
          "value": "File"
          }
  },
  {"menu": {
          "id": "file2",
          "value": "File2"
          }
  }]'
io = StringIO.new input # here a file stream is opened
loop.inject(counter: 0, string: '') do |acc|
  char = io.getc
  break if char.nil? # EOF
  next acc if acc[:counter].zero? && char != '{' # between objects
  acc[:string] << char
  if char == '}' && (acc[:counter] -= 1).zero?
    # ⇓⇓⇓ # CALLBACK, feel free to JSON.parse here
    puts acc[:string].gsub(/p{Space}+/, ' ') 
    next {counter: 0, string: ''} # from scratch
  end
  acc.tap do |result|
    result[:counter] += 1 if char == '{'
  end
end
#⇒ {"menu": { "id": "file", "value": "File" } }
#  {"menu": { "id": "file2", "value": "File2" } }

在这里,我们只是逐字节地读取流,一旦满足非常接近的花括号,我们就会发出puts。这是高效和防弹的,假设您担心输入是一个由散列组成的数组。

希望能有所帮助。

相关内容

  • 没有找到相关文章

最新更新