venerdì 17 giugno 2011

HowTo: Usare XML nel proprio programma con TinyXML

XML è bello e andrebbe usato ogni qual volta se ne ha l’occasione. Il problema spesso è avere a disposizione un parser che sia:

  • Piccolo 
  • Semplice
  • Facile da includere nel nostro progetto 

Nel mondo Open Source, putroppo, almeno una di queste tre caratteristiche è spesso assente. Vi segnalo però TinyXML, una libreria C++ veramente minuta che mantiene quel che promette.


Step 1) Scarichiamo TinyXML e includiamola nel nostro progetto C++


Il download è uno zip contenente due header e quattro file di codice. Non vi sono binari. Eh, ma allora è da ricompilare! Non proprio. Cioè, liberi di farlo, ma potete semplicemente includere i file nel vostro progetto: non richiederanno flag o altre impostazioni aggiuntive per la compilazione. Se proprio non vi va di avere qualche Kb di sorgente di terzi potete sempre creare una libreria statica (.a o .lib) e includere poi quella. Personalmente preferisco il primo metodo.


Step 2) Utilizziamo TinyXML


Tutto quel che occorre per accedere alle classi della libreria è contenuto in tinyxml.h, da cui:

#include “tinyxml.h”

La documentazione online offre numerosi esempi d’uso, ma si concentra più sulla creazione di nuovi documenti che sulla lettura di documenti esistenti. Scelta dell’autore, ma credo sia esigenza più frequente la seconda che non la prima. Inoltre, la ricchezza degli esempi può al tempo stesso risultare spaesante. In questo tutorial vediamo quindi come aprire un documento in lettura ed esplorarne la struttura. Il documento di esempio ha questa forma:

<items>
   <item id=”1”>
      <name>Item01</name>
   </item>
   <item id=”2”>
      <name>Item02</name>
   </item>
</items>

La nostra root è il nodo items, che contiene una serie di elementi item. Ogni elemento item ha un attributo id e una proprietà name.

NB: TinyXML, per mantenere la sua semplicità, deve compiere qualche rinuncia. Una di queste è il supporto ai DTD, quindi non abbiamo strumenti per verificare la validità formale del nostro XML.

Vediamo ora come fare per aprire il nostro file (items.xml) e andare a beccare l’item con id 2 per stamparne a video il nome.

TiXmlDocument doc("items.xml");
if(doc.LoadFile())
{
   TiXmlElement* root = doc.RootElement();
   TiXmlNode* item_node = root->IterateChildren(null);

   while( item_node )
   {
      TiXmlElement* item_element = item_node->ToElement();
      int item_id = 0;
      item_element->QueryIntAttribute("id", &item_id);

      if(item_id == 2)
      {
         // stampo a video il valore name di questo nodo
         TiXmlElement* name_property = item_element->FirstChild(“name”)->ToElement();
         std::cout << name_property->GetText() << endl;

         // ed esco dal while
         break;
      }

   item_node = root->IterateChildren( item_node );
   }
}

TiXmlDocument è la classe principale della libreria. Sintetizza il nostro documento xml fisico, il cui path è richiesto nel costruttore. Il metodo LoadFile è autoesplicativo e ha un booleano come valore di ritorno per verificare che l’operazione sia andata a buon fine.

Una volta che il nostro documento xml è caricato in memoria nell’oggetto doc, possiamo iniziare a giocare con i metodi della classe. In particolare dobbiamo accedere alla root (RootElement()), ottenere il primo dei nodi figli (IterateChildren(null)) e iniziare ad iterare tra tutti i figli di root finchè non troviamo quello che ci interessa. La IterateChildren accetta in input il puntatore al nodo corrente e restituisce il nodo successivo, o null in assenza di ulteriori nodi. Nella nostra prima chiamata le passiamo null, ad indicare che vogliamo accedere al primo elemento. Le chiamate successive (ultima istruzione del while) contengono invece il puntatore all’elemento corrente.

La prima istruzione del while è un cast da TiXmlNode* a TiXmlElement*, una specializzazione di TiXmlNode che ne estende le funzionalità. La documentazione dei due metodi è il posto migliore in cui scoprire le differenze e magari sbirciare l’albero di ereditarietà.

TiXmlElement ha diversi metodi per leggere un attributo, in particolare per interpretarne il valore secondo un tipo predefinito. Nel nostro caso gli id sono tutti interi, quindi usiamo senza timori QueryIntAttribute, che necessita di un puntatore ad intero per restituire il risultato della lettura.

Una volta raggiunto il nodo con l’id che ci interessa usiamo il metodo FirstChild(const char* value) per accedere al primo nodo figlio con il nome specificato: name, nel nostro caso. Otteniamo quindi il relativo puntatore e accediamo al testo tramite GetText(), stampando il valore di name a video con una buona vecchia cout.

Buon coding!


Riferimenti:


TinyXML home

Forse ti interessa anche...


Nessun commento:

Posta un commento