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