LINQ TO XML
Scritto da
Alessandro Mostarda
il
mercoledì 15 giugno 2011
Linguaggio:
•
Framework:
•
Livello: 100
Download sorgenti
Linq to XML è un sottoinsieme di LINQ che consente di
interrogare file XML sfruttando appieno la potenza di LINQ. Esso è
stato introdotto con il framework 3.5 e facilita notevolmente il
recupero, la scrittura e, soprattutto, le query su un file XML.
Linq to XML lavora completamente in memoria ed è per
questo che è possibile sfruttare la normale sintassi LINQ per
operare su un file.
Rispetto alle precedenti API (DOM) per operare su file XML, LINQ
to XML presenta una serie di vantaggi non da poco:
- L'utilizzo di metodi statici per accedere ad un file, piuttosto
che metodi di istanza.
- La possibilità di lavorare direttamente sugli ELEMENT di un
file, piuttosto che aprire il file e scorrerlo fino all'ELEMENT che
ci interessa. Ciò, ad esempio, ci consente di creare un file XML
partendo direttamente dagli ELEMENT, piuttosto che partire
dall'oggetto XDocument.
- Una maggiore semplicità nell'utilizzo dei namespace.
- La maggiore comprensione nel caso in cui si debbano mischiare
query verso DB(EntityFramework) insieme a query XML. Infatti
entrambe le tecnologie adottano LINQ , per cui un metodo che
interroga un BD e restituisce un file XML, risulterà molto più
chiaro da scrivere e comprendere.
La classe principale di LINQ to XML è XDocument, ma
abbiamo un insieme di classi, ognuna delle quali rappresenta una
parte specifica di un file XML.
- XElement, rappresenta un elemento;
- XAttribute, rappresenta un attributo;
- XNode, rappresenta il concetto(astratto) di un
nodo;
- XNamespace, rappresenta un namespace;
- XComment, rappresenta un commento;
- XDeclaration, rappresenta invece la parte
dichiarativa del file.
Ma iniziamo, ora ad addentrarci nel codice per vedere all'opera
il nostro LINQ to XML.
Come sorgente dei nostri dati utilizzeremo il seguente file:
<?xml version="1.0" encoding="utf-8" ?>
<Catalog>
<Shoe id="NKAM">
<Manufacturer>Nike</Manufacturer>
<Model>Air Max</Model>
<AvailableSize>
<Size>38</Size>
<Size>39</Size>
<Size>40</Size>
<Size>41</Size>
<Size>42</Size>
<Size>43</Size>
</AvailableSize>
<Price>78,00</Price>
</Shoe>
<Shoe id="NKSX">
<Manufacturer>Nike</Manufacturer>
<Model>Air Shoxx</Model>
<AvailableSize>
<Size>38</Size>
<Size>41</Size>
<Size>44</Size>
</AvailableSize>
<Price>67,00</Price>
</Shoe>
<Shoe id="ADGA">
<Manufacturer>Adidas</Manufacturer>
<Model>Gazelle</Model>
<AvailableSize>
<Size>38</Size>
<Size>39</Size>
<Size>42</Size>
<Size>43</Size>
</AvailableSize>
<Price>90</Price>
</Shoe>
</Catalog>
Tanto per iniziare vediamo come creare un oggetto XDocument da
un file:
C#
private Document LoadFile()
{
return XDocument.Load("Shoes.xml");
}
VB.NET
Private Function LoadFile() As XDocument
Return XDocument.Load("Shoes.xml")
End Function
Questo codice viene riportato, non tanto per la sua complessità
J, ma perchè negli esempi successivi verrà sempre utilizzato.
Un XDocument può essere caricato anche da uno stream, da un Uri,
oppure da una stringa che contiene XML.
Negli esempi che seguono, invece, vedremo l'utilizzo dei
metodi Elements, Attributes , Descendants e Nodes, i quali,
restituendo un IEnumerable, permettono di conseguenza di restituire
ulteriori nodi, elementi e/o attributi.
Ad esempio volendo ritornare l'elenco completo delle gli
articoli presente nel file XML, la sintassi sarà la seguente.
C#
public List<Shoe> GetAll()
{
var xDoc = LoadFile();
var elements = xDoc.Descendants("Shoe");
return elements.Select(s => new Shoe()
{
Id = s.Attribute("id").Value,
Manufacturer = s.Element("Manufacturer").Value,
Model = s.Element("Model").Value,
Price = Convert.ToDecimal(s.Element("Price").Value),
AvailableSize = s.Descendants("Size").Select(size => (int)size).ToList()
}).ToList();
}
VB.NET
Public Function GetAll() As List(Of Shoe) Implements ILinqToXml.GetAll
Dim xDoc = LoadFile()
Dim elements = xDoc.Descendants("Shoe")
Return elements.Select(Function(s) New Shoe With
{
.Id = s.Attribute("id").Value,
.Manufacturer = s.Element("Manufacturer").Value,
.Model = s.Element("Model").Value,
.Price = Convert.ToDecimal(s.Element("Price").Value),
.AvailableSize = s.Descendants("Size").Select(Function(size) Convert.ToInt32(size.Value)).ToList()
}).ToList()
End Function
Dall'esempio precedente si può notare l'utilizzo dei metodo
Descendants e Elements. In questo caso il metodo Descendants, torna
tutti gli elementi SHOE, anche se essi non sono figli dell'oggetto
XDocument, al quale viene applicato il metodo stesso, per cui si
deduce che tale metodo serve a trovare tutti gli elementi che hanno
il nome "Shoe" a prescindere dal livello gerarchico all'interno del
file. Infatti, se invece di Descendants avessimo usato il metodo
Elements, il risultato sarebbe stato un elenco vuoto. Il metodo
Element utilizzato per costruire l'oggetto SHOE dimostra ancora più
chiaramente tale concetto. Infatti il metodo Element torna solo i
nodi figli dell'oggetto a cui viene applicato il metodo.
Il prossimo esempio ritorna tutte gli articoli che hanno
un prezzo superiore ad un determinato valore.
C#
public List<Shoe> GetByPrice(decimal price)
{
var xDoc = LoadFile();
var elements = xDoc.Descendants("Shoe").Where(w => Convert.ToDecimal(w.Element("Price").Value > price);
return elements.Select(s => new Shoe()
{
Id = s.Attribute("id").Value,
Manufacturer = s.Element("Manufacturer").Value,
Model = s.Element("Model").Value,
Price = Convert.ToDecimal(s.Element("Price").Value),AvailableSize = s.Descendants("Size").Select(size => (int)size).ToList()
}).ToList();
}
VB.NET
Public Function GetByPrice(ByVal price As Decimal) As List(Of Common.Shoe) Implements ILinqToXml.GetByPrice
Dim xDoc = LoadFile()
Dim elements = xDoc.Descendants("Shoe").Where(Function(w) Convert.ToDecimal(w.Element("Price").Value) > price)
Return elements.Select(Function(s) New Shoe With
{
.Id = s.Attribute("id").Value,
.Manufacturer = s.Element("Manufacturer").Value,
.Model = s.Element("Model").Value,
.Price = Convert.ToDecimal(s.Element("Price").Value),
.AvailableSize = s.Descendants("Size").Select(Function(size) Convert.ToInt32(size.Value)).ToList()
}).ToList()
End Function
I 2 esempi precedenti sono già più che sufficienti a dimostrare
il funzionamento di LINQ to XML per effettuare query verso un
file.
Ma linq to XML viene utilizzato non solo in lettura, ma anche in
scrittura e quindi ora cominciamo a vedere come creare un semplice
file per poi vedere come costruirne uno partendo da zero.
C#
public string CreateXml()
{
var shoes = this.GetByPrice(70);
var xDoc = new XDocument(
new XElement("Shoes", shoes.Select(s => new XElement("shoe",
new XElement("Model", s.Manufacturer + " " + s.Model),
new XElement("Price", s.Price.ToString("#,##0.00"))))));
return xDoc.ToString();
}
VB.NET
Public Function CreateXml() As String Implements ILinqToXml.CreateXml
Dim shoes = Me.GetByPrice(70)
Dim xDoc = New XDocument(
New XElement("Shoes", shoes.Select(Function(s) New XElement("shoe",
New XElement("Model", s.Manufacturer + " " + s.Model),
New XElement("Price", s.Price.ToString("#,##0.00"))))))
Return xDoc.ToString()
End Function
La cosa che si può notare in questo esempio è anche la
chiarezza, in quanto l'indentazione facilita la comprensione del
codice. Questa cosa non era possibile con i vecchi oggetti DOM.
Ed ora veniamo alle operazione che possono effettuate sui
singoli record, ossi inserimento, modifica, cancellazione(CRUD
Operations).
L'inserimento ha un funzionamento molto simile all'esempio
precedente.
C#
public void Add()
{
var shoe = new Shoe()
{
Id = "456",
Manufacturer = "Reebok",
Model = "Pump",
Price = 34,
AvailableSize = new List<int>() { 38, 39, 48 }
};
var xDoc = this.LoadFile();
xDoc.Element("Catalog").Add(new XElement("Shoe",
new XAttribute("id", shoe.Id),
new XElement("Manufacturer", shoe.Manufacturer),
new XElement("Model", shoe.Model),
new XElement("Price", shoe.Price.ToString("#,##0.00")),
new XElement("AvailableSize",
shoe.AvailableSize.Select(s => new XElement("size", s.ToString())))));
xDoc.Save("Shoes.xml");
}
VB.NET
Public Sub Add() Implements ILinqToXml.Add
Dim shoe = New Shoe() With {
.Id = "456",
.Manufacturer = "Reebok",
.Model = "Pump",
.Price = 34,
.AvailableSize = New List(Of Integer)(New Integer() {38, 39, 48})
}
Dim xDoc = Me.LoadFile()
xDoc.Element("Catalog").Add(New XElement("Shoe",
New XAttribute("id", shoe.Id),
New XElement("Manufacturer", shoe.Manufacturer),
New XElement("Model", shoe.Model),
New XElement("Price", shoe.Price.ToString("#,##0.00")),
New XElement("AvailableSize",
shoe.AvailableSize.Select(Function(s) New XElement("size", s.ToString())))))
xDoc.Save("Shoes.xml")
End Sub
Per quanto riguarda la modifica di un elemento invece,
occorre prima di tutto individuarlo e poi utilizzare il metodo
SetElementValue per poter settare il valore
dell'elemento da variare.
C#
public void Update()
{
//Aggiorno il prezzo dell'articolo con id NKAM
var xDoc = this.LoadFile();
var shoeToUpdate = xDoc.Descendants("Shoe").FirstOrDefault(f=> f.Attribute("id").Value == "NKAM");
if (shoeToUpdate != null)
{
shoeToUpdate.SetElementValue("Price", (45).ToString("#,##0.00"));
xDoc.Save("Shoes.xml");
}
}
VB.NET
Public Sub Update() Implements ILinqToXml.Update
'Aggiorno il prezzo dell'articolo con id NKAM
Dim xDoc = Me.LoadFile()
Dim shoeToUpdate = xDoc.Descendants("Shoe").FirstOrDefault(Function(f) f.Attribute("id").Value = "NKAM")
If shoeToUpdate IsNot Nothing Then
shoeToUpdate.SetElementValue("Price", (45).ToString("#,##0.00"))
xDoc.Save("Shoes.xml")
End If
End Sub
Per terminare, invece, vediamo come eliminare uno o più
record.
C#
public void Delete()
{
//Cancello l'articolo con id NKAM
var xDoc = this.LoadFile();
xDoc.Descendants("Shoe").Where(f => f.Attribute("id").Value == "NKAM").Remove();
xDoc.Save("Shoes.xml");
}
VB.NET
Public Sub Delete() Implements ILinqToXml.Delete
'Cancello l'articolo con id NKAM
Dim xDoc = Me.LoadFile()
xDoc.Descendants("Shoe").Where(Function(f) f.Attribute("id").Value = "NKAM").Remove()
xDoc.Save("Shoes.xml")
End Sub
Da questi esempi si è potuto intuire e/o ammirare la
flessibilità e la semplicità di utilizzo di questo pezzetto di LINQ
dedicato ai file XML. Il fatto di sfruttare LINQ, che ormai è
diventato di uso comune, consente di manipolare file con una
facilità disarmante e con un buon risparmio a livello di codice
rispetto ai vari oggetti del suo predecessore DOM.
Ricordarsi però che LINQ to XML lavora in memoria, per cui per
grossi file il caricamento del file risulterà piuttosto lento
rispetto ai classici oggetti DOM, mentre per effettuare le query
poi risulteranno più veloci, L'oggetto XMLReader rimane sempre il
più veloce, per cui, in caso file di enormi dimensioni o nei
casi in cui le performance sono fondamentali, quest'ultimo rimane
sempre l'approccio migliore….
Ok ragazzi, per ora è tutto ci vediamo nei prossimi
articoli…..
Dedico questo articolo,
al mio caro papà,
che dal mese scorso non c'è
più.
Ciao Riccardo
Tags: Linq,,xml