Oltre a JSON, di cui si è discusso nella lezione precedente, uno dei formati più utilizzati per la strutturazione e l'interscambio dei dati è XML (acronimo che sta per eXtensible Markup Language). Diffuso già da diversi anni, XML può essere utilizzato per diversi scopi, e strutturato in modi più o meno standard, definendo convenzioni che hanno portato all'affermazione di formati quali RSS, SVG ed RDF/XML.
In questo articolo vedremo come utilizzare Python per effettuare il parsing di un file XML, nonché per scrivere file XML. A tale fine, utilizzeremo il modulo xml.etree.ElementTree, incluso in tutte le installazione di Python più recenti. Come nel resto della guida, utilizzeremo Python 3 come versione di riferimento del linguaggio. Dal momento che non ci soffermeremo sul formato XML, chi volesso approfondirne le caratteristiche può fare riferimento alla guida a XML di HTML.it.
Leggere file XML
Come già detto quando abbiamo affrontato la gestione dei file JSON, anche con i file XML è teoricamente possibile una gestione basata sulle API per l'interazione con i normali file di testo. Sebbene abbiamo già trattato la gestione dei file in precedenza, è bene sottolineare che ciò è poco sensato, dal momento che Python ci offre una soluzione molto più performante, finalizzata proprio all'interfacciamento con i file XML: il modulo xml.etree.ElementTree.
La prima cosa da fare è quindi importare tale modulo:
import xml.etree.ElementTree as ET
A questo punto, avremo due opzioni per la lettura dell'XML: possiamo leggere il contenuto di un file XML, oppure effettuare il parsing di una stringa contenente XML valido. Nel primo caso, procediamo come segue:
tree = ET.parse('test.xml')
root = tree.getroot()
In alternativa, se vogliamo leggere da una stringa, utilizzeremo il metodo fromstring()
:
root = ET.fromstring('Test')
In entrambi i casi, la variabile root
rappresenterà l'elemento radice del file XML. Come ogni elemento, esso include una proprietà denominata attrib
, contenente un dizionario (eventualmente vuoto) di attributi. Ogni attributo è quindi una entry del dizionario, in cui il nome dell'attributo è la chiave, ed il suo contenuto è il valore. Per semplicità, consideriamo il seguente file XML:
<root test="prova">
<elemento>Test</elemento>
</root>
In questo caso, l'elemento root
conterrà un dizionario con un solo attributo. Per accedere al valore di tale attributo, potremo procedere così:
import xml.etree.ElementTree as ET
tree = ET.parse('test.xml')
root = tree.getroot()
root.attrib['test']
Va detto, inoltre, che nel caso in cui sia specificato un namespace, questo andrà anteposto alla chiave di ogni attributo. Facciamo un esempio:
<h:table xmlns:h="http://www.w3.org/TR/html4/" h:test="prova">
<h:tr class="myclass">
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
Supponiamo di volere accedere all'attributo test
(identificato dal namespace h:
, ovvero http://www.w3.org/TR/html4/
). In questo caso, la chiave da utilizzare su Python per accedere al valore dell'attributo non sarà semplicemente test
, bensì {http://www.w3.org/TR/html4/}test
:
root.attrib['{http://www.w3.org/TR/html4/}test']
Nel seguito, per semplicità, ci riferiremo sempre ad esempi senza namespace, sebbene questa considerazione può essere utile per gestire file XML più complessi.
Considerando ora il file XML seguente, immaginiamo ora di volere accedere agli elementi figli di root
:
<root>
<elemento>Test1</elemento>
<elemento>Test2</elemento>
<elemento>Test3</elemento>
</root>
Tutti i nodi figli di root
possono essere esplorati con un semplice ciclo for
. L'esempio seguente chiarisce meglio questo concetto:
for child in root:
print(child.tag) #la proprieta' tag fornisce il nome dell'elemento, incluso l'eventuale namespace
print(child.text) #la proprieta' text fornisce il contenuto testuale dell'elemento
print(child.attrib) #ovviamente, possiamo ottenere gli attributi di ogni elemento figlio
Infine, possiamo scegliere di utilizzare la sintassi delle parentesi quadre, accedendo agli elementi figli tramite l'indice di posizione. Ad esempio, il primo figlio di root sarà accessibile tramite la sintassi root[0]
, il secondo figlio tramite root[1]
e così via.
Oltre alle funzionalità di base appena viste, ce ne sono alcune più avanzate (ad esempio implementando ricerche tramite XPath) che possono essere approfondite facendo riferimento alla documentazione ufficiale.
Modificare un file XML
Una volta caricato un file XML, possiamo modificarne il contenuto sfruttando un comodo metodo messo a disposizione da Python: il metodo write
della classe ElementTree
. Supponiamo di avere caricato il contenuto di un file XML, e di modificarlo come segue:
import xml.etree.ElementTree as ET
#Parsing da file
tree = ET.parse('test.xml')
root = tree.getroot()
root[0].text = 'Prova' #modifica del testo di un elemento
root[1].attrib['nome'] = 'tizio' #inserimento (o modifica) di un attributo
root.remove(root[2]) #rimozione di un elemento
A questo punto, usiamo il metodo write
dell'oggetto tree
per scrivere il file XML così modificato su un nuovo file:
tree.write('output.xml')
Scrivere file XML
Oltre a modificare un file XML, possiamo anche crearne uno da zero. Immaginiamo, ad esempio, di volere creare un file XML come il seguente:
<rubrica>
<persona id="1">
<nome>Vito</nome>
<cognome>Gentile</cognome>
</persona>
</rubrica>
La funzione SubElement()
permette di creare nuovo elementi a partire da un elemento dato. Ecco quindi come creare un nuovo albero XML come il precedente:
root = ET.Element('rubrica')
persona = ET.SubElement(root, 'persona')
persona.attrib['id'] = '1'
nome = ET.SubElement(persona, 'nome')
nome.text = 'Vito'
cognome = ET.SubElement(persona, 'cognome')
cognome.text = 'Gentile'
tree = ET.ElementTree(root)
tree.write('output.xml')
Abbiamo innanzitutto definito un nuovo elemento radice, creando una istanza della classe Element
. Quindi, la funzione SubElement
è utilizzata per creare gli elementi figli: il primo argomento rappresenta l'elemento genitore, mentre il secondo argomento è il nome del nuovo elemento (che è rappresentato dal valore di ritorno della funzione SubElement
). Abbiamo poi utilizzato le stesse istruzioni viste in precedenza per creare gli attributi e per valorizzare il contenuto testuale degli elementi.
Infine, è stato sufficiente creare una istanza della classe ElementTree
, mediante la quale utilizzare (come già visto) il metodo write
, che ci permette di generare il file XML appena definito.
Per approfondire i concetti visti in questa lezione, rimandiamo alla documentazione ufficiale.