我正在尝试使用sax解析器解析大型xml文件。当解析器到达一个为空的节点时,characters方法不会触发。这是一个例子...
require 'nokogiri'
class Parser < Nokogiri::XML::SAX::Document
def initialize
@count=1
end
def start_element(name, attrs = [])
puts name
end
def characters(string)
string.strip!
puts "#{@count} #{string}"
@count += 1
end
def end_element(name)
puts name
end
end
Nokogiri::XML::SAX::Parser.new(Parser.new).parse(File.open('sax_example3.xml'))
这是示例xml文档。
<?xml version="1.0" encoding="UTF-8"?>
<root>
<ISA type="array">
<ISA>
<I02>
<name>Information1</name>
<value>
<raw>00</raw>
<description></description>
</value>
</I02>
<I02>
<name>Information2</name>
<value>
<raw></raw>
<description nil="true"/>
</value>
</I02>
</ISA>
</ISA>
</root>
我必须使用sax
,因为文件的大小大约为6.5 million lines
。
我想做的是将所有name
值收集,然后将raw
值收集到单独的数组中,稍后我可以压缩两个数组以获取键值对。
我正以正确的方式来处理吗?还有其他方法吗?
编辑:
我期望的结果
array1 = ["Information1","Information2"]
array2 = ["00", ""]
所有name
值都分配给array1,raw
值分配给array2,如上所示。
我要得到的东西
array1 = ["Information1","Information2"]
array2 = ["00"]
array2
的元素数量与array1
的元素数量不同,这意味着无法将名称映射到数组。我认为这样做的原因是,如果节点为空,则不会调用characters
方法。
这是上面程序的输出(编辑上面的脚本并添加行号)
root
1
ISA
2
ISA
3
I02
4
name
5 Information1
name
6
value
7
raw
8 00
raw
9
description
description
10
value
11
I02
12
I02
13
name
14 Information2
name
15
value
16
raw
raw
17
description
description
18
value
19
I02
20
ISA
21
ISA
22
root
如您所见,在第(9&10),(16&17)和(17&18)行之间执行了start_element
和end_element
方法,但是执行了characters
方法不是。
答案 0 :(得分:1)
由于可能不会调用characters
,因此您需要自己注意<name>
和<raw>
元素。如果我们可以假设<name>
和<raw>
总是成对并以此顺序排列,那么每次遇到前者时,我们都可以创建一个新的“空”对(例如{ name: nil, raw: nil }
),并且然后在(如果有)characters
被调用时填写值:
class Parser < Nokogiri::XML::SAX::Document
def initialize(*args)
@vals = []
@current_el = nil
super
end
def start_element(el_name, attrs = [])
if el_name == "name"
@vals << { name: nil, raw: nil }
@current_el = "name"
elsif el_name == "raw"
@current_el = "raw"
else
@current_el = nil
end
end
def end_element(el_name)
if el_name == "name" || el_name == "raw"
@current_el = nil
end
end
def characters(str)
str = str.strip
if @current_el == "name"
@vals.last[:name] = str
elsif @current_el == "raw"
@vals.last[:raw] = str
end
end
def end_document
pp @vals
end
end
您可以在repl.it上看到它的运行情况(但请注意,由于Nokogiri,它永远永远需要第一次运行):https://repl.it/@jrunning/SpitefulRichLists