在Ruby中解析一个非常大的JSON文件的正确方法是什么?

Ale*_*pov 3 ruby json

我们如何解析Ruby中的json文件?

require 'json'

JSON.parse File.read('data.json')
Run Code Online (Sandbox Code Playgroud)

如果文件非常大并且我们不想立即将其加载到内存中该怎么办?那我们怎么解析呢?

mde*_*gis 5

既然你说不想立刻将它加载到内存中,那么通过块来做这件事更适合你.你可以检查yajl-ffi gem来实现这一目标.从他们的文件:

对于较大的文档,我们可以使用IO对象将其流式传输到解析器中.我们仍然需要解析对象的空间,但文档本身永远不会完全读入内存.

require 'yajl/ffi'
stream = File.open('/tmp/test.json')
obj = Yajl::FFI::Parser.parse(stream)
Run Code Online (Sandbox Code Playgroud)

但是,当从磁盘或网络流式传输小型文档时,yajl-rubygem将为我们提供最佳性能.

通过网络以小块形式到达EventMachinereceive_data循环的大量文档Yajl::FFI是唯一适合的.在EventMachine::Connection子类中我们可能有:

def post_init
  @parser = Yajl::FFI::Parser.new
  @parser.start_document { puts "start document" }
  @parser.end_document   { puts "end document" }
  @parser.start_object   { puts "start object" }
  @parser.end_object     { puts "end object" }
  @parser.start_array    { puts "start array" }
  @parser.end_array      { puts "end array" }
  @parser.key            { |k| puts "key: #{k}" }
  @parser.value          { |v| puts "value: #{v}" }
end

def receive_data(data)
  begin
    @parser << data
  rescue Yajl::FFI::ParserError => e
    close_connection
  end
end
Run Code Online (Sandbox Code Playgroud)

解析器接受JSON文档的块并解析到可用缓冲区的末尾.传入更多数据将恢复先前状态的解析.当发生有趣的状态更改时,解析器会通知所有已注册的回调事件.

事件回调是我们可以进行有趣的数据过滤并传递给其他进程的地方.上面的示例只是打印状态更改,但回调可能会以小批量查找名为rows的数组和这些行对象的进程集.通过网络流式传输的数百万行可以通过这种方式在恒定的存储空间中处理.