Analizar archivos XML grandes con Nokogiri

Analizar archivos XML grandes con Nokogiri

Analizar archivos XML es un problema bastante común. Existen muchas bibliotecas disponibles que ayudan a realizar esta tarea. Una de ellas es Nokogiri.

ANÁLISIS SAX

SAX (Simple API for XML) es una estrategia de análisis alternativa que utiliza una secuencia XML basada en eventos. Con SAX, los analizadores se mueven line-by-line, desencadenando eventos cuando se detectan elementos. Nokogiri ofrece un analizador SAX . Echemos un vistazo a un ejemplo:

SAX en Acción

Dado un ejemplo de documento XML ( './test/fixtures/example.xml' ):

<?xml version="1.0"?>
<Data>
  <Node>
    <Element Type="Boom">He's on fire!</Element>
  </Node>
  <Node>
    <Element Type="Shaka">Jams it in!</Element>
  </Node>
  <Node>
    <Element Type="Laka">Razzle Dazzle!</Element>
  </Node>
</Data>

El análisis SAX de Nokogiri nos obliga a definir un documento analizador SAX que define los métodos de gestión de eventos:

class Parser < Nokogiri::XML::SAX::Document
  def start_element(name, attrs = [])
    # Manejar cada elemento, esperando el nombre y los atributos
  end
 
  def characters(string)
    # Los caracteres entre el elemento de inicio y el final esperado como una cadena
  end
 
  def end_element(name)
    # Dado el nombre de un elemento una vez alcanzada su etiqueta de cierre
  end
end

Para comenzar el análisis SAX, usaríamos nuestro analizador de esta manera:

Nokogiri::XML::SAX::Parser.new(Parser.new).parse(File.open('./test/fixtures/example.xml'))

 Nokogiri transmitirá nuestro archivo XML, línea por línea, llamando a start_element , characters y end_element ya llama a la etiqueta de apertura, el contenido, y la etiqueta de cierre. Por ejemplo, el analizador SAX está utilizando nuestra línea <Element Type="Boom">He's on fire!</Element>, esto es lo que obtenemos si tenemos una unión ( binding.pry o byebug ) en cada método:

# In start_element:
pry(main)> name
=> "Element"
pry(main)> attrs
=> [["Type", "Boom"]]
 
# In characters:
pry(main)> string
=> "He's on fire!"
 
# In end_element
pry(main)> name
=> "Element"

 Si estás más preocupado por los atributos o el contenido de elementos específicos en lugar del contexto o la estructura de un nodo o elemento en particular, utiliza SAX!

ANÁLISIS DOM

Debido a que XML es un formato de documento utilizado para capturar un árbol de datos estructurado, casi siempre significa que la estructura es importante. Al analizar XML a través de SAX, se pierde esa estructura de árbol. Cuando XML se analiza a través de un analizador DOM, se obtiene un conjunto totalmente transitable de objetos que refleja la estructura del archivo XML original. El analizador DOM de Nokogiri es rápido y efectivo.

DOM en Acción

El analizador DOM primario en Nokogiri se llama así:

Nokogiri::XML(File.open('./test/fixtures/example.xml'))
# or..
Nokogiri::XML.parse(File.open('./test/fixtures/example.xml'))

Obtendrás un objeto Nokogiri::XML::Document (la representación DOM del archivo XML). Puedes inspeccionar el nombre, atributos y nodos secundarios desde cualquier punto en el DOM. También puedes utilizar XPath para buscar el DOM.

Para la mayoría de los casos, esto es probablemente todo lo que necesitas. Sin embargo, si estás preocupado por la memoria, existe Nokogiri::XML::Reader  que transmite los XML nodo por nodo.

Nokogiri::XML::Reader en Acción

El lector de XML utiliza ligeramente más memoria que el analizador SAX, pero la ventaja aquí es que conservas la estructura XML del nodo. Si tuviéramos millones de nodos <Node>, pero quisiéramos procesar cada nodo según se lee, los podríamos extraer uno a uno con el lector y hacer que algún otro objeto maneje el procesamiento de cada nodo :

class NodeHandler < Struct.new(:node)
  def process
    # Node processing logic
  end
end
 
Nokogiri::XML::Reader(File.open('./test/fixtures/example.xml')).each do |node|
  if node.name == 'Node' && node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT
    NodeHandler.new(
      Nokogiri::XML(node.outer_xml).at('./Node')
    ).process
  end
end

Los elementos node   de los rendimientos Reader para nuestro bloque son Nokogiri :: XML :: Reader, objetos que representan un tipo de apertura XML ( Nokogiri :: XML :: :: Reader TYPE_ELEMENT ) denominado  nodo. Esto es lo que tenemos dentro de nuestro bloque if:

pry(main)> node.name
=> 'Node'
pry(main)> node.node_type
=> 1 # This means we're at an opening tag for a node
pry(main)> node.inner_xml
=> "n    <Element Type="Boom">He's on fire!</Element>n  "
pry(main)> node.outer_xml
=> "<Node>n    <Element Type="Boom">He's on fire!</Element>n  </Node>"

 Los objetos de lectores representan nodos y nos dejan inspeccionar el contenido XML que lo rodea como las cadenas. El método outer_xml  nos da una cadena XML que captura exactamente un <Node> completo - justo lo que necesitamos. Para realizar nuestro procesamiento, utilizamos el analizador normal DOM para convertir de nuevo esa cadena XML en un XML DOM,  y a continuación, coger el nodo que queremos:

Nokogiri::XML(node.outer_xml).at('./Node')

 Esto devuelve un tipo de elemento Nokogiri::XML::Element con una interfaz agradable:

# Asumiendo nuestro Nokogiri::XML::Element se almacena en una variable llamada node:
pry(main)> node.name
=> "Node"
pry(main)> element = node.at('./Element')
=> #<Nokogiri::XML::Element:0x3fd5d9a8fcc0 name="Element" attributes=[#<Nokogiri::XML::Attr:0x3fd5d9a8fbf8 name="Type" value="Boom">] children=[#<Nokogiri::XML::Text:0x3fd5d9a8ef78 "He's on fire!">]>
pry(main)> element.name
=> "Element"
pry(main)> element['Type']
=> "Boom"
pry(main)> element.content
=> "He's on fire!"

CONCLUSIÓN

Hay un número de maneras de analizar XML en Ruby, pero cuando estás tratando con archivos XML particularmente grandes, prueba Nokogiri. Dependiendo de tus necesidades, mira el Nokogiri::XML::SAX::Parser o el  Nokogiri::XML::Reader

Comentarios

Sin comentarios
Ha habido un error en el envío
Comentario enviado. Será revisado por la moderación antes de ser publicado.

Deja tu comentario

Tu nombre:
Tu email:
Tu comentario: