In questo capitolo vedremo quali strumenti Ruby mette a disposizione per la manipolazione dell'XML e approfondiremo YAML già visto nella prima parte di questa guida.
Cominciamo con REXML
, che è parte della libreria standard, e permette l'utilizzo sia delle API DOM sia di quelle SAX. Per approfondire il linguaggio XML è utile consultare XML.HTML.it.
Nel corso del capitolo utilizzeremo un file XML di esempio di nome cd.xml
che contiene questi dati:
<negozio nome="CD House"> <genere nome="Stoner"> <cd asin="B00000JBDE"> <titolo>Frequencies From Planet Ten</titolo> <autore>Orange Goblin</autore> <anno>1997</anno> </cd> <cd asin="B000WM72FC"> <titolo>Witchcult Today</titolo> <autore>Electric Wizard</autore> <anno>2007</anno> </cd> </genere> <genere nome="Colonne sonore"> <cd asin="B00005O6PA"> <titolo>Amelie</titolo> <autore>Yann Tiersen</autore> <anno>2001</anno> </cd> </genere> </negozio>
Com'è nello stile di questa guida entriamo subito nel vivo dell'esempio. Sorvoliamo la creazione di codice XML, che è un'operazione tediosa, e vediamo come estrarre dei dati dal file XML in una struttura di tipo REXML::Document
sulla quale poi operare per ricavare tutti gli elementi.
require 'REXML/document' xmlfile = File.new("cd.xml") doc = REXML::Document.new(xmlfile) doc.root.each_element do |gen| puts gen.attributes["nome"] gen.each_element do |cd| cd.each_element do |val| puts " #{val.name}: #{val.text}" end end end
Dopo aver aperto il file XML e averlo passato a REXML::Document
iniziamo a iterare sulla struttura partendo dall'elemento radice (doc.root
) utilizzando il metodo each_element
della classe Element
.
Ad ogni chiamata di each_element
scendiamo di un livello nella gerarchia del nostro file XML, ovvero passiamo da <negozio>
a <genere>
e quindi a <cd>
dove ci fermiamo per ricavare i nomi e i valori degli elementi. Il risultato è il seguente:
Stoner titolo: Frequencies From Planet Ten autore: Orange Goblin anno: 1997 titolo: Witchcult Today autore: Electric Wizard anno: 2007 Colonne sonore titolo: Amelie autore: Yann Tiersen anno: 2001
Oltre a each_element
la libreria mette a disposizione numerosi metodi per navigare tra gli elementi, abbiamo ad esempio next_element
, previous_element
ed anche variazioni di each_element
come each_element_with_attribute
e each_element_with_text
.
Esaminiamo ora un esempio che fa uso del linguaggio XPath per navigare attraverso gli elementi del nostro file XML.
require 'REXML/document' xmlfile = File.new("cd.xml") doc = REXML::Document.new(xmlfile) REXML::XPath.each(doc, '//genere/cd') do |cd| puts "#{cd.elements["titolo"].text} - #{cd.elements["autore"].text}" end
Qui troviamo each
, che itera su ogni elemento specificato. Oltre a questo abbiamo due metodi altrettanto interessanti: first
che restituisce il primo elemento che verifica il percorso indicato e match
che restituisce un array di tutti gli elementi che verificano il path di ricerca impostato.
Esempio di ricerca con match
REXML::XPath.match(doc, '//[@nome="Stoner"]')
Concludiamo con un esempio di parsing di tipo SAX2:
require 'REXML/parsers/sax2parser' xmlfile = File.new("cd.xml") doc = REXML::Parsers::SAX2Parser.new(xmlfile) doc.listen(:characters, ["titolo"]) do |titolo| puts titolo end doc.parse
Dopo aver importato il file nel giusto oggetto, creiamo attraverso il metodo listen
un evento characters
, come secondo argomento passiamo un array contenente i tag che ci interessa utilizzare all'interno del blocco. Oltre a "characters" gli altri possibili eventi dall'ovvio significato sono: :start_element
, :end_element
, :cdata
, :start_prefix_mapping
, :end_prefix_mapping
, :processing_instruction
, :doctype
, :attlistdecl
, :elementdecl
, :entitydecl
, :notationdecl
, :xmldecl
, :comment
.