Un documento XML viene rappresentato in memoria attraverso la classe XmlDocument. Tutti gli oggetti .NET che hanno a che fare con il trattamento di documenti XML sono contenuti all'interno del namespace System.Xml. Non so il perchè, ma tutte le volte che devo caricare un documento XML sono convinto di usare il metodo Load come se fosse statico, mentre è di istanza: quando me lo ricorderò, sarà ormai troppo tardi! :-) Abbiamo due strade: una prevede l'utilizzo del metodo Load, l'altra il metodo LoadXml.
Con quest'ultimo metodo possiamo caricare un documento XML direttamente da una stringa, come qui sotto:
StringBuilder bld = new StringBuilder();
bld.AppendLine("<books>");
bld.AppendLine("<book>");
bld.AppendLine("<author>Terry Brooks</author>");
bld.AppendLine("<title>Le Pietre Magiche</title>");
bld.AppendLine("<pages>450</pages>");
bld.AppendLine("<year>1998</year>");
bld.AppendLine("</book>");
bld.AppendLine("</books>");
string xml = bld.ToString();
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
Usando uno StringBuilder costruisco un documento XML minimale, ma tecnicamente valido. Usando LoadXml, carichiamo la stringa in un XmlDocument. Se il documento XML non dovesse essere well-formed, il framework solleva l'exception XmlException. Il metodo Load invece carica l'XML da un file fisico su disco, da uno Stream, da un TextReader o da un XmlReader.
Una volte che il documento è stato caricato in memoria, il FW ci mette a disposizione tutta una serie di metodi che ci permettono di navigare il documento o di ricercare informazioni al suo interno. Il metodo GetElementsByTagName ci ritorna un'istanza di XmlNodeList che contiene l'elenco di tutti i tag XML con un certo nome, specificato nella chiamata.
XmlNodeList nodes = doc.GetElementsByTagName("book");
In questo caso, l'oggetto nodes contiene in questo caso un solo oggetto XmlNode. Se vogliamo fare ricerche un po' più complesse usando XPath, abbiamo due metodi: SelectNodes (che ritorna un XmlNodeList che contiene i nodi che soddisfano i criteri di ricerca espressi in XPath) e SelectSingleNode (che invece ritorna il primo XmlNode trovato). XmlDocument espone le proprietà FirstChild e LastChild che ritornano rispettivamente il primo e l'ultimo XmlNode presente nel documento caricato. La proprietà ParentNode permette di ottenere l'istanza di XmlNode padre rispetto a quella corrente. La proprietà NextSibling ritorna un XmlNode che è il "fratello" successivo dell'XmlNode corrente, mentre PreviousSibling percorre l'XML in senso opposto. Se avessi tanti <book> inseriti nel blocco XML sopra, potrei saltare da uno all'altro semplicemente 1) ottenendo un riferimento XmlNode al primo <book> inserito nel documento e 2) chiamando NextSibling per balzare da uno all'altro.
Ovviamente, ci sono tutta una serie di funzionalità anche per modificare il documento, aggiungendo e rimuovendo XmlNode dove necessario. AppendChild, CreateNode, ImportNode (per importare XmlNode da un documento all'altro), InsertAfter, InsertBefore sono alcuni dei metodi esposti da XmlDocument. RemoveAll, RemoveChild e ReplaceChild sono piuttosto auto-esplicativi.
L'oggetto XmlDocument eredita da XmlNode. Effettivamente, ne condivide buona parte dell'interfaccia. Basta dare un'occhiata ai membri esposti da XmlNode per farsene un'idea. La prima cosa che salta all'occhio è che XmlNode non espone eventi, mentre XmlDocument sì: se volessi monitorare in qualche modo tutto quello che accade ad un certo nodo XML (modifiche al suo InnerText, per esempio), quindi, non posso sottoscrivere un evento specificatamente per quel nodo, ma quelli relativi all'intero documento. Il FW2.0 ci permette di gestire una piccolo set di eventi relativi ai nodi XML: li potete vedere elencati qui (cancellazione, inserimento e modifiche ai nodi del documento). Gli event handler fanno uso della classe XmlNodeChangedEventArgs che espone una serie di membri per capire esattamente cosa è successo e dove.