Nokogiri SAX忽略空字符

时间:2019-06-24 11:03:57

标签: ruby xml

我正在尝试使用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_elementend_element方法,但是执行了characters方法不是。

1 个答案:

答案 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