Ruby on rails 在Ruby中逐个对象解析巨大的json对象
问题 我有一个json文件,由大量的小json对象组成。现在,如果我尝试用常规方法解析它,将文件读取到内存中,然后调用对其进行的任何json解析(例如json.parse或Oj.parse),它将消耗我所有的系统可用内存,并且不会完成 我想要什么? 通过流解析它的某种方式,每次它完成一个对象时,它都会用该对象回调一个函数。有了这个,我相信内存的使用将是非常低和恒定的 我迄今为止所取得的成就 我检查了两个gem(和),并使用yajl找到了以下解决方案:Ruby on rails 在Ruby中逐个对象解析巨大的json对象,ruby-on-rails,ruby,json,parsing,stream,Ruby On Rails,Ruby,Json,Parsing,Stream,问题 我有一个json文件,由大量的小json对象组成。现在,如果我尝试用常规方法解析它,将文件读取到内存中,然后调用对其进行的任何json解析(例如json.parse或Oj.parse),它将消耗我所有的系统可用内存,并且不会完成 我想要什么? 通过流解析它的某种方式,每次它完成一个对象时,它都会用该对象回调一个函数。有了这个,我相信内存的使用将是非常低和恒定的 我迄今为止所取得的成就 我检查了两个gem(和),并使用yajl找到了以下解决方案: def post_init @parse
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)
我会忘记规则,采用以下方法:
#!/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" } }
#/usr/bin/env ruby
测试需要“stringio”
输入='[{“菜单”:{
“id”:“文件”,
“值”:“文件”
}
},
{“菜单”:{
“id”:“文件2”,
“值”:“文件2”
}
}]'
io=StringIO.new input#此处打开一个文件流
loop.inject(计数器:0,字符串:“”)do | acc|
char=io.getc
如果字符为零,则中断?#EOF
下一个acc如果acc[:计数器]。零?&&char!='对象之间的{'#
acc[:string]我会忘记规则,采用以下方法:
#!/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" } }
!/usr/bin/env ruby
测试需要“stringio”
输入='[{“菜单”:{
“id”:“文件”,
“值”:“文件”
}
},
{“菜单”:{
“id”:“文件2”,
“值”:“文件2”
}
}]'
io=StringIO.new input#此处打开一个文件流
loop.inject(计数器:0,字符串:“”)do | acc|
char=io.getc
如果char.nil,则中断?#EOF
下一个acc如果acc[:counter].zero?&&char!='{'#在对象之间
acc[:字符串]@Jordan您提供的示例有什么问题?由callback决定如何处理恶意数据。您希望任何json解析器都能对这种类型的输入进行smth有意义的处理吗?我代码中的输入不是恶意数据,它是100%有效的json。任何正确的json解析器都能处理它。哦,真的,对不起。附加检查“我们是内部引号”是必需的。然后您还需要检查转义引号。这是JSON解析器不使用正则表达式的原因。我知道JSON解析器不使用正则表达式的原因。好吧,我没有将其称为“防弹”的原因但是对于OP的目标,而不是学术研究,这个解决方案可能是最好和最简单的。顺便说一句,这个方法不使用regexps。AFAIK,所有流解析器都使用相同的方法,因为构建语法/使用访问者在大输入上可能会非常昂贵。@Jordan您提供的示例有什么问题吗?这取决于回调to决定如何处理恶意数据。你认为任何json解析器都会对这种类型的输入进行有意义的smth处理吗?我代码中的输入不是恶意数据,而是100%有效的json。任何正确的json解析器都能处理它。哦,真的,对不起。额外检查“我们在引号内”是必需的。然后您还需要检查转义引号。这是JSON解析器不使用正则表达式的原因。我知道JSON解析器不使用正则表达式的原因。好的,我称之为“防弹”失败但是对于OP的目标,而不是学术研究,这个解决方案可能是最好和最简单的。顺便说一句,这个方法不使用regexps。AFAIK,所有流解析器都使用相同的方法,因为构建语法/使用访问者在大输入上可能非常昂贵。