Chapter 3. LINQ to XML

LINQ to XML is a new in-memory XML programming API to work against XML data. There are different ways of creating XML trees in .NET. LINQ to XML is the new method of creating and manipulating XML data through .NET. The properties and methods of LINQ help us to navigate and manipulate the XML elements and attributes.

LINQ uses a feature called functional construction for creating the XML tree. The .NET compiler translates XML literals into calls to the equivalent XML constructor to build the objects. LINQ to XML also provides the object model for creating and manipulating the XML data. This feature also integrates well in the .NET framework. The namespaces which supports LINQ and LINQ to XML are as follows:

using System.Linq;
using System.Xml.Linq;

The namespaces have changed from the May CTP. We need to include these namespaces into our project to take advantage of the LINQ to XML features. There are a lot of XML-specific query operators that come with LINQ, which we can use for querying and manipulating the XML elements and values, as we do in our normal SQL queries. Visual Studio also provides IntelliSense for accessing some of the query methods and properties that will make a developer's life easier.

In this chapter, we will see how to use LINQ to XML features for navigating and manipulating the XML elements and attributes.

Features

Using LINQ queries, we can query data from different sources, whether it is relational or XML. LINQ to XML is the XML programming language built on the .NET Language Integrated Query framework. Using LINQ to XML we can create, update, and delete XML elements in the XML tree. It also provides streaming, and the transformation and querying feature, similar to XQuery and XPath.

XQuery is a language which can query structured or semi-structured XML data. XQuery is based on the XPath language. It has the ability to iterate, sort, and construct the necessary XML. If the XML document is stored in the SQL server database, which has support for XML, the document can be queried using XQuery. The result of the XQuery can be typed or un-typed. The type information of the result is based on the type, which is specified in the XML schema language.

LINQ provides query features to write queries against XML, as we do normally with the relational data model. LINQ provides different query operators, such as projections, aggregates, partitioning, grouping, and conversion.

The developers can take advantage of writing LINQ queries instead of writing normal SQL queries, to reduce their coding work and get high performance. We can take advantage of .NET 3.5 features, as LINQ and LINQ to XML are integrated in the .NET framework. If we are using LINQ with Visual Studio, we can also use IntelliSense and statement completion feature provided by Visual Studio for easy programming.

The results of the queries from LINQ, and LINQ to XML, are strongly typed. This increases the robustness of the application. All the errors are caught at compile time instead of runtime, as it happens when we write SQL queries while programming.

Technical architects or software designers, can design their own LINQ providers for the data source they use. By implementing a new provider, they can give the query, language support to the data model they use. They can also take advantage of using some of the features of the .NET 3.0 framework.

Class Library

All the classes of LINQ to XML are present in the System.Xml.Linq namespace; we have seen most of the LINQ to XML classes in the examples given in the previous sections. Following is the list of LINQ to XML classes present in the System.Xml.Linq namespace and the hierarchy of classes.

Classes and Hierarchy

The major high level classes defined in LINQ to XML are as follows:

  • XName

  • XNamespace

  • XNode

  • XDeclaration

  • XAttribute

  • XObject

The XNode class has the second level of classes, listed as follows:

  • XText

  • XComment

  • XContainer

  • XDocumentType

  • XProcessingInstruction

The XContainer has two more classes further down as:

  • XElement

  • XDocument

Below is the diagrammatic representation of classes defined in LINQ to XML:

Classes and Hierarchy

We will see details of the important classes used, to work with the elements and attributes in XML documents, in the following sections.

XElement Class

This is one of the fundamental classes of all LINQ to XML classes. The entire XML tree is created using this class, as we have seen in the functional construction section. XML data manipulation and traversing through the XML tree happens using XElement object, as seen in the following code:

new XElement("Nutrition","Calories:290",
new XAttribute("TotalFat", "18g"))

XAttribute Class

These are the name or value pair associated with an XML element, but they are not derived from nodes. Working with attributes is similar to working with XElements. Using functional construction, attributes are added to the elements to form the XML tree. The XAttribute class has one constructor which takes two parameters. The first parameter specifies the name, and the second specifies the content, as shown in the following code:

new XElement("Nutrition","Calories:290",
new Xattribute("TotalFat", "18g"))

XDocument Class

Similar to W3C DOM, the XDocument is a container for the XML document. The XDocument can contain one root XElement, XComment, XDeclaration, and XProcessingInstruction. For example:

XmlDocument Icecreams = new XmlDocument();

Other Classes

An XNode represents any item that is represented as a node in the XML tree. XElements, XComments and XDocument, XDocumentType, and XProcessingInstructions and XText are some of the nodes represented in the XML tree.

The XComments class is used for adding comments to the XML. XProcessingInstruction is for providing additional information to the application that processes the XML data.

Following is the code for adding some of the nodes to the XML:

// Adding Declaration, Comments and PI
XDocument IcecreamsDocument = new XDocument
(
new XDeclaration("1", "utf-8", "yes"),
new XComment("XML data Manipulation using LINQ"),
new XProcessingInstruction("Instructions", "12345-67890"),
new XElement("Icecreams",
new XElement("Icecream",
new XElement("Name", "Chocolate Fudge Icecream"),
new XElement("Ingredients", "cream, milk, sugar, corn syrup,
cellulose gum, mono and diglycerides..."),
new XElement("Cholesterol", "50mg"),
new XElement("TotalCarbohydrates", "35g"),
new XElement("Protein","4g")
)
)
);

Here we have the XDocument object, IcecreamsDocument, which has a declaration, comment, and processing instruction added to it. XElement is used for creating the nodes of the XML document. The output for the above code will be as follows:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--XML data Manipulation using LINQ-->
<?Instructions 12345-67890?>
<Icecreams>
<Icecream>
<Name>Chocolate Fudge Icecream</Name>
<Ingredients>cream, milk, sugar, corn syrup, cellulose gum, mono
and diglycerides...</Ingredients>
<Cholesterol>50mg</Cholesterol>
<TotalCarbohydrates>35g</TotalCarbohydrates>
<Protein>4g</Protein>
</Icecream>
</Icecreams>

LINQ to XML with Other XML Technologies

When we talk about using XML, the first thing that comes to our mind is the XML DOM. We use XML programming API of W3C Document Object Model (DOM). XML programming means either traversing or manipulating data in the XML tree. These are the only things that we normally do with an XML document. In case of XML DOM, we follow the "bottom-up" approach of creating the XML document using XmlDocument object first and then building the XML elements and attributes. We do programming with elements and attributes. Coming up is an example of creating an XML document using XML DOM, which is the standard way of doing it using ADO.NET 2.0.

The following code creates an XML document Icecreams to store different varieties of ice-creams as XML elements. Each ice-cream element contains many elements to hold the properties of ice-cream. Each XML element is created separately and then added to the main Icecream element as children. After adding all the properties as elements, the main Icecream element itself is added to the document as an XML element. Like this, we can keep on adding the elements to the document and build the XML tree.

XmlDocument Icecreams = new XmlDocument();
XmlElement Name = Icecreams.CreateElement("Name");
Name.InnerText = "Chocolate Fudge Icecream";
XmlElement Ingredients =
Icecreams.CreateElement("Ingredients");
Ingredients.InnerText = "cream, milk, sugar, corn syrup,
cellulose gum, mono and diglycerides...";
XmlElement SaturatedFat = Icecreams.CreateElement("SaturatedFat");
SaturatedFat.SetAttribute("type", "SaturatedFat");
SaturatedFat.InnerText = "20g";
XmlElement TransFat = Icecreams.CreateElement("TransFat");
TransFat.SetAttribute("type", "TransFat");
TransFat.InnerText = "5g";
XmlElement OtherFat = Icecreams.CreateElement("OtherFat");
OtherFat.SetAttribute("type", "OtherFat");
OtherFat.InnerText = "10g";
XmlElement Cholesterol = Icecreams.CreateElement("Cholesterol");
Cholesterol.InnerText = "50mg";
XmlElement TotalCarbohydrates = Icecreams.CreateElement("TotalCarbohydrates");
TotalCarbohydrates.InnerText = "35g";
XmlElement Protein = Icecreams.CreateElement("Protein");
Protein.InnerText = "4g";
XmlElement Icecream = Icecreams.CreateElement("Icecream");
Icecream.AppendChild(Name);
Icecream.AppendChild(Ingredients);
Icecream.AppendChild(SaturatedFat);
Icecream.AppendChild(TransFat);
Icecream.AppendChild(OtherFat);
Icecream.AppendChild(TotalCarbohydrates);
Icecream.AppendChild(Protein);
Icecreams.AppendChild(Icecream);
Icecreams.Save(@"C:Icecreams.XML");

LINQ to XML simplifies this XML document creation process. We don't need to create the XML Document object to create elements and attributes of the XML tree. Using LINQ, we can create the XML tree directly using the XElement object. Here is how we can construct the same code as seen previously, using LINQ:

XElement IceCreams =
new XElement("Icecreams",
new XElement("Icecream",
new XElement("Name", "Chocolate Fudge Icecream"),
new XElement("Ingredients", "cream, milk, sugar, corn
syrup, cellulose gum, mono and diglycerides..."),
new XElement("TotalFat", "20g",
new XAttribute("SaturatedFat", "8g"),
new XAttribute("TransFat", "12g")),
new XElement("Cholesterol", "50mg"),
new XElement("TotalCarbohydrates", "35g"),
new XElement("Protein","4g")
)
);
IceCreams.Save(@"C:Icecreams2.XML");

In the example, we don't see an XmlDocument object for creating the XML tree. This is the main advantage of .NET 3.5. In W3C DOM, everything happens in the context of the document object; we can directly use the XElement object for creating elements and attributes and we can even save this to a file. We don't need to depend on the XmlDocument object. Following is the code for loading an XML document using the W3C DOM.

XmlDocument DOMLoadIcecreams = new XmlDocument();
DOMLoadIcecreams.Load(@"C:Icecreams.xml");

The equivalent code would be:

XElement LoadIcecreams = XElement.Load(@"c:Icecreams.xml");

XElement is the main object used for loading the XML as well as saving the XML. The XmlDocument object is nowhere in the picture when we use LINQ.

Following is a list of features that differentiates LINQ from W3C DOM. Most of the features are well supported by LINQ.

Feature

Support by LINQ

Namespaces

LINQ to XML provides a better approach for namespaces than DOM. An XML name consists of an XML namespace, which is also called XML namespace URI. XML namespace is similar to the namespace used in .NET Framework and the purpose is the same. It helps us uniquely identify elements and attributes in the XML document without having any name conflicts in different parts of the XML document.

DTD Constructs

LINQ to XML does not support XmlEntityReferences. When an XML tree is populated, all DTD entities are expanded. This simplifies the XML construction.

XPath

LINQ to XML does not support Xpath queries. Instead we can use LINQ features.

XmlDocumentFragment

LINQ to XML does not support XmlDocumentFragment class. It is handled by the query result, which is of the IEnumerable<XNode> type.

XPathNavigator

No support.

BaseURI

No support. LINQ to XML does not store any URI information.

InnerXml

LINQ to XML provides a read only XML property which returns the InnerXml and also through Parse method. DOM provides support for getting and setting InnerXML.

Annotations

LINQ to XML elements support an extensible set of annotations, whereas the XmlElement does not support this. This is useful to add additional information to the element.

Schema Information

LINQ to XML nodes are extensible via annotations. LINQ to XML does not provide any schema information.

LINQ with XmlReader

LINQ to XML is implemented on top of the XmlReader, but each of these is used for different purposes.

XmlReader provides a fast-forward only, and non-cached access to XML data. It can be used in places where we have to navigate through a large amount of XML data without any manipulations. XmlReader will be helpful performance-wise, but will not be able to update the data as it returns read only data.

If we have to deal with different sources of data involving data manipulation, we can go for LINQ to XML, as it provides an in-memory XML programming.

LINQ with XSLT

XSLT (Extensible Stylesheet Language Transformation) is the definition language for XML data presentation and transformations. XSLT is derived from the XSL (Extensible Stylesheet Language). The presentation gives a specific format or style to the XML document, and transformation, reading the nodes of the XML tree, and converting that to the required tree.

XSLT and LINQ to XML are two different approaches for getting the XML transformations. Both are flexible and powerful ways of doing the transformation.

In case of XSLT, it is an independent programming language, which is rule based and has a declarative approach. XSLT is used in situations where multiple web applications have the same transformation style, but possibly have different sources of data. Performance wise, it helps a lot but the disadvantage is that the developers cannot make use of the C# or VB.NET. programming language The developer has to depend on these two different programming languages, which is a complex process. Also, it is difficult to maintain.

LINQ to XML provides the feature of functional construction, by which we can construct the XML objects dynamically, by pulling the data from different sources. Constructing transformation is very easy, which helps the user not to depend on other programming language, like XSLT. This reduces a lot of maintenance and development work.

LINQ with MSXML

MSXML can be used from any programming language that supports COM. MSXML is a COM-based technology for processing XML. MSXML provides a native implementation of the DOM with support for XSLT and XPath. It is not recommended for use in managed code, based on the Common Language Runtime (CLR)

Functional Construction

Functional construction is how we construct the XML tree. Using LINQ, we can construct the entire XML tree with only the XElement class. XElement has three constructors for constructing the XML tree:

  1. XElement(XName name)—creates an XML element with the name specified in the XName parameter.

  2. XElement(XName name, object content)—creates an element with the name specified in the XName parameter, and the content passed in by the object content.

  3. XElement(XName name, params object[] content)—creates an element with the name specified in the XName parameter, and the second parameter is the child element created by the paremeter list. It could be any valid object, which can be a child of an element. It can be another XElement or an XAttribute or any of the following:

    • A string, which is added as text content. This is the recommended pattern to add a string as the value of an element; the LINQ implementation will create the internal XText node.

    • An XText, which can be a string or CData value, added as child content. This is mainly useful for CData values; using a string is simpler for ordinary string values.

    • An XElement, which can be added as a child element.

    • An XAttribute, which can be added as an attribute.

    • An XProcessingInstruction or XComment, which is added as child content.

    • An IEnumerable, which is enumerated, and these rules are applied recursively.

    • null, which is ignored.

Let us see how we can build an XML tree using the functional construction method. The code for building an XML tree, which has details of Icecream, is as follows:

Icecreams.Add(
new XElement("Icecream",
new XElement("Name", "Vanilla Icecream"),
new XElement("Ingredients", "vanilla extract,
guar gum, cream, nonfat milk, sugar,
locust bean gum, carrageenan,
annatto color..."),
new XElement("Cholesterol", "65mg"),
new XElement("TotalCarbohydrates", "26g"),
new XElement("Protein", "4g",
new XAttribute("Vitamin A","1g"),
new XAttribute("Calcium", "2g"),
new XAttribute("Iron", "1g")),
new XElement("TotalFat", "16g",
new XAttribute("SaturatedFat","7g"),
new XAttribute("TransFat", "9g"))
)
);

The first XElement object, Icecreams, uses the following constructor:

XElement(XName name, params object[] content)

The second parameter takes the number of XElement objects as content. This will be the child of this parent element that gets created. The child element in turn can have any number of XElements. So the Icecreams element has the child element Icecream, which in turn has many elements such as Name, Ingredients, Cholesterol, and TotalCarbohydrates.

The XElement name is constructed with the use of the constructor:

XElement(XName name, object content)

This takes the XName for the name of the element and content for the element. This element is added as one of the child elements for the Icecream element:

new XElement("Name", "Vanilla Icecream")

The output of this will be as follows:

<Name>Vanilla Icecream</Name>

If we have to create an element with only the XName, and without the content, we can use the following constructor:

XElement(XName name)

For example, ,XElement Icecream = new XElement("Icecream"), will give output:

<Icecream/>

Let us save this XElement using the Save method and see the XML tree created by the functional construction using XElement.

Icecreams.Save(@"C:Icecreams");

The output of the above created XElement Icecreams would be:

<?xml version="1.0" encoding="utf-8"?>
<Icecreams>
<Icecream>
<Name>Vanilla Icecream</Name>
<Ingredients>vanilla extract, guar gum, cream, nonfat milk,
sugar, locust bean gum, carrageenan,
annatto color...
</Ingredients>
<Cholesterol>65mg</Cholesterol>
<TotalCarbohydrates>26g</TotalCarbohydrates>
<Protein VitaminA="1g" Calcium="2g" Iron="1g">4g</Protein>
<TotalFat SaturatedFat="7g" TransFat="9g">16g</TotalFat>
</IcecreamTwo>
</Icecreams>

To create the above XML file, we have used only the functional construction feature of LINQ.

XML Names

An XML name consists of an XML namespace, which is also called XML namespace URI. XML namespace is similar to the namespace used in .NET framework and the purpose is the same. It helps us uniquely identify the elements and attributes in the XML document, without having any name conflicts in different parts of the XML document. The XML names are represented by an XNamespace object and a local name. For example, if you want to create an element Icecreams, you might want to create it under a namespace called http://yourcompany.com/Icecreams.

To make the XML document more understandable, we can use a prefix for the namespaces. Prefixes allow us to create a shortcut for an XML namespace. LINQ simplifies this by introducing the XNamespace object and a local name. So when reading in XML, each XML prefix is resolved to its corresponding XML namespace. The class that represents XML names is XName, which consists of XNamespace and a local name. The code below is an example of defining the namespace and using it in the node:

XNamespace nspace = "http://yourcompany.com/Icecreams/";
XElement Icecreamss = new
XElement("{http://yourcompany.com}IcecreamsList");

The string representation of an XName is referred to as an expanded name. An expanded name looks like this:

{NamespaceURI}LocalName

Instead of constructing a namespace object, we can use the expanded name, but we need to type the namespace every time we need the XName. To avoid this, we can take advantage of the language feature of defining the namespace and use it with the local name:

XNamespace nspace = "http://yourcompany.com/";
XElement Icecreamss = new
XElement("{http://yourcompany.com}IcecreamsList",
new XElement(nspace + "Icecream",
new XElement(nspace + "Name",
"Rum Raisin Icecream"),
new XElement(nspace + "Ingredients", "Rum, guar
gum, nonfat milk, cream, alomnds,
sugar, raisins, honey, chocolate,
annatto color..."),
new XElement(nspace + "Protein", "6g",
new XAttribute("Iron", "4g")),
new XElement(nspace + "TotalCarbohydrates", "28g")
)
);
Icecreamss.Save(@"c:Icecreamss.xml");

The previous example has one expanded name used with the Icecreamss element. All other elements use the namespace defined as nspace. It is not that the entire node should be part of the same namespace. They can be different from one another. For example, change the namespace value nspace to http://yourcompany.com/Icecream/, and see the difference in namespace value for the elements. It will be as follows:

<?xml version="1.0" encoding="utf-8"?>
<IcecreamsList xmlns="http://yourcompany.com">
<Icecream xmlns="http://yourcompany.com/Icecream">
<Name>Rum Raisin Icecream</Name>
<Ingredients>Rum, guar gum, nonfat milk, cream, alomnds, sugar,
raisins, honey, chocolate, annatto color...
</Ingredients>
<Protein Iron="4g">6g</Protein>
<TotalCarbohydrates>28g</TotalCarbohydrates>
</Icecream>
</IcecreamsList>

Loading and Traversing XML

Now that we've seen how to create a basic XML tree and the basic classes of LINQ to XML and its hierarchy, let's take a more detailed look at loading XML data and finding our way through it.

Loading XML

There are different ways of loading XML data. The XML data can be in the form of a file, or a string, or any other supported formats. Using LINQ to XML, we can load XML data through the XElement object. For example, to load XML data from a file, we can use the Load method of the XElement object, as given below:

// Loading XML
XElement LoadIcecreams = XElement.Load(@"c:Icecreams.xml");

If the XML data is in the form of a string, we can load it through XElement by using the Parse method. For example, the code for loading Icecreams from string is as follows:

XElement LoadIcecreamsfromString = XElement.Parse(
@"<Icecream>
<Name>Rum Raisin Icecream</Name>
<Cholesterol>49mg</Cholesterol>
<VitaminA type=""VitaminA"">2g</VitaminA>
<VitaminC type=""VitaminC"">1g</VitaminC>
<Iron type=""Iron"">4g</Iron>
<TotalCarbohydrates>[email protected]</TotalCarbohydrates>
</Icecream>");

Traversing XML

In this section, we will see how we can walk through the XML tree, which is also called traversing through XML. There are several methods provided by LINQ to get all the children of the XElement.

Nodes() is the method used mainly for traversing. This returns the IEnumerable<object> because the XML can have a different type and it also gives the sequential access to items in the collection. For example, let us have the following XML file called Icecreams.xml.

<?xml version="1.0" encoding="utf-8"?>
<Icecreams>
<Icecream>
<Name>Chocolate Fudge Icecream</Name>
<Ingredients>cream, milk, sugar, corn syrup, cellulose gum, mono
and diglycerides...</Ingredients>
<Cholesterol>50mg</Cholesterol>
<TotalCarbohydrates>35g</TotalCarbohydrates>
<Protein VitaminA="3g" Iron="1g" Calcium="3g">5g</Protein>
<TotalFat SaturatedFat="9g" TransFat="11g">20g</TotalFat>
</Icecream>
<!-- This is the text added at the bottom of the XML file -->
</Icecreams>

Load this Icecreams.xml file into an XElement object, and then using Nodes() method, we will traverse through the XML to get the details. The following code returns the node values from the XML file:

// Loading XML
XElement LoadClassicIcecreamsFile =
XElement.Load(@"C:LoadClassicIcecreams.xml");
// Traversing XML
foreach (XNode nod in LoadClassicIcecreamsFile.Nodes())
{
Console.WriteLine(nod.ToString());
}

The previous code gives the following output:

<?xml version="1.0" encoding="utf-8"?>
<Icecreams>
<Icecream>
<Name>Chocolate Fudge Icecream</Name>
<Ingredients>cream, milk, sugar, corn syrup, cellulose gum, mono
and diglycerides...</Ingredients>
<Cholesterol>50mg</Cholesterol>
<TotalCarbohydrates>35g</TotalCarbohydrates>
<Protein VitaminA="3g" Iron="1g" Calcium="3g">5g</Protein>
<TotalFat SaturatedFat="9g" TransFat="11g">20g</TotalFat>
</Icecream>
<!-- This is the text added at the bottom of the XML file -->
</Icecreams>

The code outputs all element values and the text added to the XML document at the bottom. We can also filter the nodes using the name and type. For example, the following code will bring only the nodes of the XElement type and displays the XML value:

foreach (XNode nod in
LoadClassicIcecreamsFile.Nodes()
OfType<XElement>())
{
Console.WriteLine(nod.ToString());
}

The equivalent of the above code is as follows:

foreach (XElement nod in
LoadClassicIcecreamsFile.Elements())
{
Console.WriteLine(nod.Value);
}

We can also get a particular element based on the name. We can use the overloaded method Elements (XName), which takes XName as parameter. For example, the following code is used for getting the Name element of the icecreams.xml file.

foreach (XElement nod in LoadClassicIcecreamsFile.Elements("Icecream"))
{
Console.WriteLine(nod.Element("Name").Value);
}

If the XElement node has more than one child element, we can use the Elements method to traverse through the child elements. If we have only one child element, we can directly point to that using the Element method. In the above code we are looping through the Icecreams element as it has many children. Inside the loop, we have directly used the Element method to get the Name element from the tree as there is no child element for the Name. Following are some more examples of traversing through the XML and getting information about the nodes:

foreach (XElement node in
LoadClassicIcecreamsFile.Elements("Icecreams"))
{
// Value of Protein element
Console.WriteLine("Protein : " +
node.Element("Protein").Value + "
");
// Parent to Parent of Protein element
Console.WriteLine("GrandParent of Protein Element : "
+ node.Element("Protein").
Parent.Parent.Name.ToString() + "
");
// Type of Protein Element
Console.WriteLine("Protein : " + node.Element("protein")
.NodeType.ToString() + "
");
// Next node to Last Name node
Console.WriteLine("Next node after Protein : " +
node.Element("Protein")
.NextNode.ToString() + "
");
// Last node in the Icecream
Console.WriteLine(@"Last node in the Icecream element
: " + node.LastNode.ToString() + "
");
// Value of the type attribute in teh Phone element
Console.WriteLine("Value of the type attribute in the
TotalFat Element : " + node.Element("TotalFat")
.Attribute("SaturatedFat")
.Value.ToString() + "
");
// Are there any attributes to the Employee element
Console.WriteLine("Icecream Element has any attributes : " + node.HasAttributes.ToString() + "
");
}

In the above example, we have many child elements under the Icecreams element. If we have to get all the elements after a particular element, or before a particular element, then we can use ElementsAfterSelf and ElementsBeforeSelf methods. For example, following is the code to get all elements after the Name element and all elements before the Protein element under the Icecreams element:

// Using ElementsAfterSelf()
string afterName = "";
string beforeProtein = "";
IEnumerable<XElement> elementsAfterName =
IcecreamsDocument.Element("Icecreams")
.Element("Icecream").Element("Name").ElementsAfterSelf();
foreach (XElement ele in elementsAfterName)
{
afterName = afterName + ele.Value;
}
// Using ElementsBeforeSelf()
IEnumerable<XElement> elementsBeforeProtein =
IcecreamsDocument.Element("Icecreams")
.Element("Icecream").Element("Protein")
.ElementsBeforeSelf();
foreach (XElement eleBefore in elementsBeforeProtein)
{
beforeProtein = beforeProtein + eleBefore.Value;
}

The return value of ElementsAfterSelf() is of the type IEnumerable by which we can enumerate over the elements that are siblings to this node and appear after this node in terms of XML order. The final output of the afterName string will have information of all elements after the Name element:

<Ingredients>cream, milk, sugar, corn syrup, cellulose gum, mono and diglycerides...</Ingredients><Cholesterol>50mg</Cholesterol><TotalCarbohydrates>35g</TotalCarbohydrates><Protein>4g</Protein>

The final output of the beforeProtein string will have the following information of all elements before the Protein element.

<Name>Chocolate Fudge Icecream</Name><Ingredients>cream, milk, sugar, corn syrup, cellulose gum, mono and diglycerides...</Ingredients><Cholesterol>50mg</Cholesterol><TotalCarbohydrates>35g</TotalCarbohydrates>

Data Manipulation

Data manipulation is one of the most important steps in the application development. Whether we deal with the relational data or XML data, we do some kind of data manipulation for keeping the information up-to-date. Normally, we insert, update, and delete the information. LINQ to XML provides a lot of features and methods for XML data manipulation. As we know XElement plays an important role in LINQ as it has many methods to add, remove, or update a node in the XML tree. We should take care of handling the NullreferenceExceptions that occur when we try to manipulate an element which does not exist in the XML document.

Inserting or Adding Elements to XML

Let's use the following code to create the XML file, with the details of an ice-cream. It consists of the ice-cream's name, dietary information, and ingredients.

XDocument IcecreamsDocument = new XDocument(
new XDeclaration("1", "utf-8", "yes"),
new XComment("XML data Manipulation using LINQ"),
new XProcessingInstruction
("Instructions", "12345-67890"),
new XElement("Icecreams",
new XElement("Icecream",
new XElement("Name", "Chocolate Fudge
Icecream"),
new XElement("Ingredients", "cream, milk,
sugar, corn syrup, cellulose
gum, mono and diglycerides..."),
new XElement("Cholesterol", "50mg"),
new XElement("TotalCarbohydrates", "35g"),
new XElement("Protein","4g")
)
)
);

We will save the XML file using the Save method of the XElement object IcecreamsDocument.Save(@"C:IcecreamsDocument.XML"); and this would produce the following XML file:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--XML data Manipulation using LINQ-->
<?Instructions 12345-67890?>
<Icecreams>
<Icecream>
<Name>Chocolate Fudge Icecream</Name>
<Ingredients>cream, milk, sugar, corn syrup, cellulose gum, mono
and diglycerides...</Ingredients>
<Cholesterol>50mg</Cholesterol>
<TotalCarbohydrates>35g</TotalCarbohydrates>
<Protein>4g</Protein>
</Icecream>
</Icecreams>

Let us see how we can include new elements to the above given Icecream element. We can do this by calling the Add method of the XElement, and pass the new element information as a child to the Icecream element. The first step is to create the new XElements such as Calcium, Iron, and VitaminA.

XElement Calcium = new XElement("Calcium", "7g");
XElement Iron = new XElement("Iron", "6g");
XElement VitaminA = new XElement("VitaminA", "3g");

Then we need to add these elements to the existing elements in the Icecreams element. This step adds the Calcium content of ice-cream as the new element to the Icecream element, which is in the Icecreams element, which in turn is inside the IcecreamsDocument document. The Calcium element will be added as the last element to the Icecream element.

IcecreamsDocument.Element("Icecreams")
.Element("Icecream").Add(Calcium);

Now, let us add the next element, Iron content of ice-cream to the Icecream element, but we shall not add this at the end of the Icecream element, but just before the Calcium element. We need to use the AddBeforeSelf method on the Calcium element by passing the Iron element as parameter.

IcecreamsDocument.Element("Icecreams").Element("Icecream").Element("Calcium").AddBeforeSelf(Iron);

We have added the Iron and Calcium elements to the Icecream element. This time we have to add the VitaminA content of Icecream as an element after the Calcium element, so that the elements will be in order. Just like we called the AddBeforeSelf method to the Clacium element, we have to call the AddAfterSelf method on the Calcium element and pass the VitaminA element as parameter, so that it will get added after the Calcium element.

IcecreamsDocument.Element("Icecreams").Element("Icecream").Element
("Calcium").AddAfterSelf(VitaminA);

After adding all the above elements, the resultant XML would be as follows:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--XML data Manipulation using LINQ-->
<?Instructions 12345-67890?>
<Icecreams>
<Icecream>
<Name>Chocolate Fudge Icecream</Name>
<Ingredients>cream, milk, sugar, corn syrup, cellulose gum, mono
and diglycerides...</Ingredients>
<Cholesterol>50mg</Cholesterol>
<TotalCarbohydrates>35g</TotalCarbohydrates>
<Protein>4g</Protein>
<Iron>6g</Iron>
<Calcium>7g</Calcium>
<VitaminA>3g</VitaminA>
</Icecream>
</Icecreams>

In the previous examples, we have one Icecream element present in the XML. If we have to add details of one more ice-cream to the above XML, we can create a new XElement, which contains the new ice-cream details, and then add it to the main element IcecreamsDocument. For example, let us create a new element of type Icecream with the following details:

XElement NewIcecreamtoAdd = new XElement("NewIcecream",
new XElement("Name", "Vanilla Icecream"),
new XElement("Ingredients", "vanilla extract,
guar gum, cream, nonfat milk,
sugar,locust bean gum, carrageenan,
annatto color..."),
new XElement("Cholesterol", "65mg"),
new XElement("TotalCarbohydrates", "26g"),
new XElement("Protein", "4g",
new XAttribute("VitaminA", "1g"),
new XAttribute("Calcium", "2g"),
new XAttribute("Iron", "1g")),
new XElement("TotalFat", "16g",
new XAttribute("SaturatedFat", "7g"),
new XAttribute("TransFat", "9g"))
);

Add the previous element to the IcecreamsDocument document as:

IcecreamsDocument.Element("Icecreams").Add(NewIcecreamtoAdd);

Now the resulting XML will contain both the ice-creams details as shown below:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--XML data Manipulation using LINQ-->
<?Instructions 12345-67890?>
<Icecreams>
<Icecream>
<Name>Chocolate Fudge Icecream</Name>
<Ingredients>cream, milk, sugar, corn syrup, cellulose gum, mono
and diglycerides...</Ingredients>
<Cholesterol>50mg</Cholesterol>
<TotalCarbohydrates>35g</TotalCarbohydrates>
<Protein>4g</Protein>
<Iron>6g</Iron>
<Calcium>7g</Calcium>
<VitaminA>3g</VitaminA>
</Icecream>
<NewIcecream>
<Name>Vanilla Icecream</Name>
<Ingredients>vanilla extract, guar gum, cream, nonfat milk,
sugar, locust bean gum, carrageenan, annatto
color...</Ingredients>
<Cholesterol>65mg</Cholesterol>
<TotalCarbohydrates>26g</TotalCarbohydrates>
<Protein VitaminA="1g" Calcium="2g" Iron="1g">4g</Protein>
<TotalFat SaturatedFat="7g" TransFat="9g">16g</TotalFat>
</NewIcecream>
</Icecreams>

In all the examples that we have examined until now, for adding or inserting new elements to the exisiting XML, we saw the way in which it succeeds every time. But what if we do not have the parent element to which we are adding the new element? For example, let us try to add the element Iron before the element VitaminE, which does not exist in our XML example, above.

IcecreamsDocument.Element("Icecreams").Element("Icecream")
.Element("VitaminE").AddBeforeSelf(Iron);

LINQ will try to find the element VitaminE in the XML; if not found, a NullReferenceException will be thrown. So we need to take care of handling the NullRefernceException.

Inserting or Adding XML Attributes

Adding attributes is similar to adding elements to the XML tree. We can use the same functional construction to add attributes. Let us take some different XML data, instead of the same ice-creams we saw in the previous example. Let us take different varieties of ice-creams. We can add new attributes to the elements using the same functional construction method we used for adding elements. The code given below shows an example of adding attributes to the elements using functional construction. We will add the VitaminA attribute with value 2g, Iron attribute with value 1g to the Protein element. Similarly, we also have two attributes under the element TotalFat. All these attributes are added at the time of adding elements.

XElement ClassicIcecreams =
new XElement("Icecreams",
new XElement("IcecreamOne",
new XElement("Name", "Chocolate Fudge Icecream"),
new XElement("Ingredients", "cream, milk, sugar,
corn syrup, cellulose gum..."),
new XElement("Cholesterol", "50mg"),
new XElement("TotalCarbohydrates", "35g"),
new XElement("Protein",
new XAttribute("VitaminA","3g"),
new XAttribute("Iron", "1g")),
new XElement("TotalFat",
new XAttribute("SaturatedFat","9g"),
new XAttribute("TransFat", "11g"))
)
);

Suppose we wanted to add a new attribute to an existing element in an existing XML document. There is another method of adding attributes to the XML nodes. We can create the attribute objects and then add it to the elements, as follows:

XAttribute attrTyp = new XAttribute("Calcium", "1g");
ClassicIcecreams.Element("IcecreamOne")
.Element("Protein").Add(attrTyp);

Or we can also add the attribute, as follows:

ClassicIcecreams.Element("IcecreamOne").Element("Protein")
.Add(new XAttribute("Calcium", "1g"));

It is not guaranteed that we will always add attributes to the correct elements. Sometimes, we may make the mistake of adding attributes to an element which does not exists in the XML document. In the next example, I will try to add the Calcium attribute to the TotalProtein element, which does not exist in our XML document.

ClassicIcecreams.Element("IcecreamOne").Element("TotalProtein")
.Add(new XAttribute("Calcium", "1g"));

When the code is executed, we will get an exception of type NullReferenceException. So we need to take care of these exceptions when we manipulate the data in the XML document.

Deleting XML

We have seen inserting the XML elements and attributes. We should also be able to delete the existing elements and attributes. Let us say we have two more ice-creams added to the above ClassicIcecreams XML document, which we saw earlier. We will see how to delete the IcecreamTwo element from the ClassicIcecreams XML document.

//Deleting Elements
ClassicIcecreams.Element("IcecreamTwo").Remove();

We can use the Remove method of the element to remove a particular element. If we want to remove all elements under a particular element, we can use RemoveAll()

ClassicIcecreams.Element("IcecreamTwo").RemoveAll();

The above code will remove all the elements under the IceCreamTwo element, but not the IcecreamTwo element.

We can also use RemoveContent() and RemoveAnnotation() to remove the content and annotations from the XML.

Even if we delete elements, we should take care of the NullreferenceExceptions that will occur when we try to remove an element which does not exists in the XML document.

Updating XML

LINQ provides many different ways to update the existing XML data. We can use the following methods to update the elements and element values. We can change the value of an element by using the Value property of the Element object, as shown below:

ClassicIcecreams.Element("IcecreamTwo")
.Element("Cholesterol").Value = "69mg";

Or we can also use SetElement method to change the value of the element as follows:

ClassicIcecreams.Element("IcecreamOne")
.SetElementValue("Protein", "5g");

We can also create a new XElement and then replace that with the one that exists in the XML document. For example:

XElement prc = new XElement("TotalCarbohydrates", "28mg"); ClassicIcecreams.Element("IcecreamTwo").ReplaceWith(prc);

The entire name of the element can also be changed using the Name property, as follows:

ClassicIcecreams.Element("IcecreamOne").Name = "Icecream1";

Deleting XML Attributes

Similar to deleting the XML element, we can delete the attributes using the Remove method of the attribute object.

//Deleting attributes
ClassicIcecreams.Element("IcecreamOne").Element("Protein").Attribute("Calcium").Remove();

In the previous statement, the Calcium attribute for the Protein element in the IcecreamOne element is removed by calling the Remove method.

Attributes can also be removed by using the RemoveAttributes method of an element, which will remove all the attributes under that element. The following example will remove all the attributes under the Protein element, which is under the IcecreamOne element.

ClassicIcecreams.Element("IcecreamOne").Element("Protein")
.RemoveAttributes();

Updating XML Attributes

Similar to updating the XML element, we can update the properties of the XML attributes. For example, the code below shows the method of updating the value Calcium attribute under the element Protein.

//Setting Attribute value
ClassicIcecreams.Element("IcecreamOne").Element("Protein")
.Attribute("Calcium").Value = "2g";

The following code is the equivalent of the previous code with a different value:

ClassicIcecreams.Element("IcecreamOne").Element("Protein")
.SetAttributeValue("Calcium", "3g");

Outputting and Streaming XML

For saving XML details in a file, we can directly use the Save method of XElement by passing the name of the file in which the XML has to be stored. The Save method requires the file parameter. To save the Icecreams XML tree created earlier in a file, we can use the following method, which saves the XML tree data to the C:Icecreams.xml file.

ClassicIcecreams.Save(@"C:IceCreams.XML");

We can also use the XmlWriter for outputting the XML data into a file. For example, following is the code which writes the XML tree after the Icecreamone element within the Icecreams element.

XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.ConformanceLevel = ConformanceLevel.Auto;
settings.CloseOutput = false;
// Write out the Icecreamone node tree
XmlWriter writer = XmlWriter.Create(@"C:Ice.xml",
settings);
ClassicIcecreams.Element("IcecreamOne").WriteContentTo(writer);
writer.Flush();
writer.Close();

First we need to create the XmlWriter object that points to the XML file in which we have to store the XML data. If required, we can change the settings for the writer according to your needs. We need to use the WriteContentTo method on the element from which we want the XML data to be sent to the writer. The name of the writer object is passed as parameter to the WriteContentTo method.

Streaming XML

XML streaming is very useful when serializing objects. For example, let us say we have an array of instances of object of type Icecream. Let us see how we can serialize some of the objects to XML.

IcecreamList[] ListofIcecreams;

LINQ to XML provides XStreamingElement for serializing the elements directly instead of creating the tree and then serializing it. If we use use XElement in the case of StreamingElement, it will create the XElement tree and iteraten through the elements. The process of creating the tree and iterating through the elements is eliminated by using XStreamingElement. Each XStreamingElement saves itself to the output stream. But if you see the end result, it will be the same in the cases of XElement and XStreamingElement.

Create the class, IcecreamList, as shown below:

class IcecreamList
{
public string flavor;
public string servingSize;
public double price;
public string nutrition;
public IcecreamList(string flv, string srvS, double prc,
string nut)
{
flavor = flv;
servingSize = srvS;
price = prc;
nutrition = nut;
}
};

Now declare an array of object of type IcecreamList class and initialize.

IcecreamList[] ListofIcecreams = new IcecreamList[2];
ListofIcecreams[0] = new IcecreamList("Vanilla", "Half
Cup", 11, "Calories:250");
ListofIcecreams[1] = new IcecreamList("Strawberry", "Half
Cup", 15, "Calories:230");

Now using the XStreamingElement, serialize the objects to XML and then save it to a file.

XStreamingElement str =
new XStreamingElement("ListofIcecreams",
from cre in ListofIcecreams
select new XStreamingElement("Icecream",
new XStreamingElement("Flavor", cre.flavor),
new XStreamingElement("Price", cre.price)
));
str.Save(@"C:streamFile.xml");

Querying XML

Querying is a very important feature of LINQ when compared to the other XML technologies. We might have used and written a lot of SQL queries to manipulate and use the relational data. LINQ gives us the feature of querying XML. LINQ provides different operators that are similar to the SQL queries. We will see more details about the query operators in Chapter 7. LINQ provides the feature of querying details from different data models in a single query. We will see some of those through examples in the following sections.

In LINQ, methods can also be called to perform some operations. The query operators are the methods which can be operated on any object that implements the IEnumerable<T> class. This way of calling query methods can be referred to as Explicit Dot Notation.

Query Operators

We have different types of operators that we can use in LINQ on XML. We will be seeing more of these operators in Chapter 7. In that chapter, we will see the details of classifications and what each one of these classifications mean and the usage of each operator. Here we will see how we can make use of these operators against XML data. These operators are classified as follows:

  • Projection operators

  • Partitioning operators

  • Join operators

  • Grouping operators

  • Conversion operators

  • Aggregate operators

Out of all these operators, there are a few operators which are common for all queries. They are where, select, OrderBy, GroupBy, and SelectMany.

We will see more about each one of these operators in detail, with examples in Chapter 7, Standard Query Operators.

Queries

Let us take an example of new XML data that has details of different ice-creams.

XElement Icecreams =
new XElement("Icecreams",
new XElement("Icecream",
new XComment("Cherry Vanilla Icecream"),
new XElement("Flavor", "Cherry Vanilla"),
new XElement("ServingSize", "Half Cup"),
new XElement("Price", 10),
new XElement("Nutrition",
new XElement("TotalFat", "15g"),
new XElement("Cholesterol", "100mg"),
new XElement("Sugars","22g"),
new XElement("Carbohydrate", "23g"),
new XElement("SaturatedFat", "9g"))));
Icecreams.Add(
new XElement("Icecream",
new XComment("Strawberry Icecream"),
new XElement("Flavor", "Strawberry"),
new XElement("ServingSize", "Half Cup"),
new XElement("Price", 10),
new XElement("Nutrition",
new XElement("TotalFat", "16g"),
new XElement("Cholesterol", "95mg"),
new XElement("Sugars","22g"),
new XElement("Carbohydrate", "23g"),
new XElement("SaturatedFat", "10g"))));

In the above XML, we have the details of two different ice-creams. Add some more ice-creams' details for better understandable results of the following queries. All of the operators that we use in queries are defined under the System.Linq namespace.

We will build a query to fetch ice-creams from the above list that have a price value equal to 10. We will also display the results by giving the flavours in order, and all the names in uppercase letters. Following is the query to get the result using the where, select, and OrderBy operators. We have also used the direct method ToUpper() to change all the letters to uppercase. The result will contain a list of ice-creams, ordered according to the flavour, as the Orderby operator is used against the element Flavour.

XElement IcecreamsList = new XElement("IcecreamsList",
(from c in Icecreams.Elements("Icecream")
where (c.Element("Price").Value == "10")
orderby c.Element("Flavour").Value select new XElement("Icecream",
c.Element("Flavour").Value.ToUpper())));

Let us assume the above query returns ten records. Now, if I would like to take records from second to fifth in order, leaving the other records, we would have to make use of the Skip() and Take() operators. The following code, shows how we can apply these operators in the above query.

XElement NewIcecreamList = new XElement("IcecreamsList",
(from c in Icecreams.Elements("Icecream")
where (c.Element("Price").Value == "10")
orderby c.Element("Flavour").Value
select new XElement("Icecream", c.Element("Flavour").Value.ToUpper().Skip(1).Take(4))));

The query operators are the methods that can be operated on any objects that implement IEnumerable<T> class. We will see how we can create an object, make it IEnumerable, and use query operators to query the XML.

First, we'll create the class and include variables corresponding to the elements in the XML. We'll create a constructor to initialize all the variables of the class.

class NewIcecreamList
{
public string flavor;
public string servingSize;
public double price;
public string nutrition;
public IcecreamList(string flv, string srvS,
double prc, string nut)
{
flavor = flv;
servingSize = srvS;
price = prc;
nutrition = nut;
}
};

After creating the class, we construct a query using the above class. The query should hold the list of ice-creams, and their details. It should be of the type IEnumerable<IcecreamList>.

// Simple Query
IEnumerable<IcecreamList> IcrmList =
from c in Icecreams.Elements()
select new IcecreamList(
(string)c.Element("Flavor"),
(string)c.Element("ServingSize"),
(double)c.Element("Price"),
(string)c.Element("Nutrition")
);

We retrieve details of ice-creams from the IcrmList variable, which is of type IEnumerable<IcecreamList>, and display that in a rich text box, which is added in the form. This code will give a list of ice-creams with details such as Flavour, ServingSize, Price, and Nutrition.

foreach (IcecreamList p in IcrmList)
Console.WriteLine(p.flavor + ":" + p.servingSize +
":" + p.price + ":" + p.nutrition + "
");

If we don't have values for any element in the XML, how do we handle it? For example, in the previous XML data, the Nutrition value is missing and whenever the Nutrition value is empty we should display null in that. In this case, the query would be as follows:

XElement Icecreams2 = new XElement("Icecreams2",
from c in Icecreams.Elements("Icecream")
select new XElement("Icecream",
(string)c.Element("Flavor"),
(string)c.Element("ServingSize"),
(string)c.Element("Price"),
c.Elements("Nutrition").Any() ?
new XElement("Nutrition", c.Elements("Nutrition").ToString()):null
)
);

The Any() operator is used here to check if the element is empty, or whether there is any value in it. If the element is not empty then return the null value. If it is present, return the element which is similar to Nutrition with the same name.

Ancestors and Descendants

These are the methods to get particular element's ancestors and the descendants in the XML tree structure. We can get this for any element, whatever its level may be. The first line is to get the TotalFat element from the Icecreams tree. Using the Ancestors methods, we can get the ancestors of the TotalFat element.

XElement Totalfat = Icecreams.Descendants("TotalFat").First();
foreach (XElement ele in Totalfat.Ancestors())
{
Console.WriteLine(ele.Name.LocalName);
Console.WriteLine("
");
}

Similar to Ancestors, we can also get the list of elements which are descendant to a particular element in an XML tree. In the above examples, we have seen a lot of descendants to the Icecreams element and also we have many descendants to the Nutrition element. Using the following code, we can retrieve the descendant elements of the Nutrition element in the XML tree.

XElement Nutrition = Icecreams.Descendants("Nutrition").First();
foreach (XElement ele in Nutrition.Descendants())
{
Console.WriteLine(ele.Name.LocalName);
}

The ancestors and descendants are a very interesting feature, which helps us to retrieve a particular type of elements from the XML tree. For example, in our XML tree, we have different types of ice-creams. Let us say the customer wants to know all the flavours available. In that case, we can use a simple query to list the ice-cream flavours available, shown in the following code:

IEnumerable<string> strList =
from flv in Icecreams.Descendants("Flavor")
select (string)flv;
foreach ( string val in strList)
{
Console.WriteLine(val + " 
");
}

The list will have values like Cherry Vanilla and Strawberry.

XML Transformation

The most common way of transforming XML in any programming language is to use XSLT. In the case of LINQ to XML, the functional construction plays a major role in transforming the XML data. We can easily build the XML tree by fetching details from other XML or sources of data. We will take the Icecream list example used in the Queries section. The Icecreams list has details of ice-creams such as Flavour, ServingSize, Price, and Nutrition; with Nutrition having many elements in it. Using these details, let's say we want to build another list only having the flavour and the nutrition details, and let's call the list IcecreamsNutritionDetails. Let us see how we can build this using the functional construction and using the Icecreams XML.

XElement IcecreamsNutritionDetails = new XElement("IcecreamsNutritionDetails",
from c in Icecreams.Elements("Icecream")
orderby c.Element("Flavor").Value
select new XElement("Icecream",
c.Element("Flavor"),
c.Element("Nutrition"))));

This query builds the IcecreamsNutritionDetails list using the details in Icecreams XML. We only extracted details like Flavour and Nutrition. In the above example, the IcecreamsNutritionDetails element is root of the XML, which holds element details. The next element in the XML tree is the Icecream element, which holds details of individual ice-cream items.

In the above example, the query fetches details from the other XML for constructing a new XML. There could be a possibility that the same details might be required in another part of the application. Not only that; we might need more readability to the code that we write. In this case, we can make use of functions in the queries. We have to break up the query and move part of the query to a function. This will also break the complex query into simpler functions. Let us break the above query as follows:

XElement IcecreamsNutritionDetails = new XElement("IcecreamsNutritionDetails",
GetIcecreamsNutritionDetails(Icecreams));

Here the root element is the same, but the construction part is moved to a function called GetIcecreamsNutritionDetails and is used in the query. Following is the function which constructs the elements using the Icecreams XML.

public IEnumerable<XElement> GetIcecreamsNutritionDetails( XElement Icecreams)
{
return from c in Icecreams.Elements("Icecream")
orderby c.Element("Flavor").Value
select new XElement("Icecream",
c.Element("Flavor"),
c.Element("Nutrition"));
}

The return type of the function is IEnumerable of type XElement. We can break down the queries to any level depending on its complexity.

Dictionaries

Dictonaries in .NET represents a generic collection of key/value pairs. Each element in a dictionary is a key/value pair where the key is the unique identifier.

Convert Dictionary to XML

It is possible to convert this kind of data structure to XML, and XML as back to a different data structure. In this section, we will see some examples of converting dictionaries to XML and XML to dictionaries.

Below is the code sample of a new dictionary that holds names of four different varieties of ice-creams.

// Create a new Dictionary and add different types of Icecreams
Dictionary<string, string> dictIcecream = new Dictionary<string, string>();
dictIcecream.Add("Icecream1", "Cherry Vanilla Icecream");
dictIcecream.Add("Icecream2", "Strawberry Icecream");
dictIcecream.Add("Icecream3", "Chocolate Fudge Icecream");
dictIcecream.Add("Icecream4", "Banana Split Icecream");

Using LINQ we can retrieve information from the dictionary and construct an XML. For example, following is a query that fetches information from the above dictionary and constructs an XML tree. The key in the key/value pair of the dictionary is used as the name of the XML element, and the value in the key/value pair is used as the XML element value. The value is taken from the dictionary using the key.

// Create XML using XElement and get details from the above dictionary
XElement Icecreams = new XElement("Icecreams",
from key in dictIcecream.Keys
select new XElement(key, dictIcecream[key])
);

The Icecream element will have the full XML details fetched by the query. Now the following code displays the Icecream element:

// display the details of the Icecreams elements
Console.WriteLine(Icecreams.ToString());

When the above code is executed, the output will be the following XML:

<Icecreams>
<Icecream1>Cherry Vanilla Icecream</Icecream1>
<Icecream2>Strawberry Icecream</Icecream2>
<Icecream3>Chocolate Fudge Icecream</Icecream3>
<Icecream4>Banana Split Icecream</Icecream4>
</Icecreams>

Create Dictionary from XML

In the previous section, we have seen the construction of XML using dictionary data. In this section we will see how we can create a dictionary from the XML data. Following is the code that creates the XML element, containing four different ice-cream varieties.

// XML element containing different Icecreams
XElement Icecreams = new XElement("Icecreams",
new XElement("Icecream1", "Cherry Vanilla Icecream"),
new XElement("Icecream2", "Strawberry Icecream"),
new XElement("Icecream3", "Chocolate Fudge Icecream"),
new XElement("Icecream4", "Banana Split Icecream")
);

The following code creates a dictionary to hold the values.

// Create a new dictionary
Dictionary<string, string> dictIcecreams = new Dictionary<string, string>();

Now retrieve all the element details from the Icecream element and add them to the dictionary one-by-one. The name of the element will be the key, and the value of the element will be the value of the key in the dictionary.

// Retrieving the detail from the above XElement and add it to the dictionary foreach (XElement ele in Icecreams.Elements())
dictIcecreams.Add(ele.Name.LocalName, ele.Value);

Now loop through the dictionary according to the number of keys in the dictionary and display their details.

// Get the details from dictionary and display it to view
foreach (string str in dictIcecreams.Keys)
Console.WriteLine(str + ": " + dictIcecreams[str]);

The output of the above code will be as follows:

Icecream1: Cherry Vanilla Icecream
Icecream2: Strawberry Icecream
Icecream3: Chocolate Fudge Icecream
Icecream4: Banana Split Icecream

Writing XML as Text Files and CSV Files

As we saw in the previous section, we can convert XML to a different data structure and different data structure to XML. For example, the following code creates an XML tree with Icecreams as the root element:

// Create XML Tree using XElement
XElement ClassicIcecreams =
new XElement("Icecreams",
new XElement("Icecream",
new XElement("Name", "Chocolate Fudge Icecream"),
new XElement("Cholesterol", "50mg"),
new XElement("TotalCarbohydrates", "35g"),
new XElement("Protein",
new XAttribute("VitaminA", "3g"),
new XAttribute("Iron", "1g")),
new XElement("TotalFat",
new XAttribute("SaturatedFat", "9g"),
new XAttribute("TransFat", "11g"))
) );
// Add new type of Icecream to the existing XML
ClassicIcecreams.Add(
new XElement("Icecream",
new XElement("Name", "Vanilla Icecream"),
new XElement("Cholesterol", "65mg"),
new XElement("TotalCarbohydrates", "26g"),
new XElement("Protein", "4g",
new XAttribute("VitaminA", "1g"),
new XAttribute("Calcium", "2g"),
new XAttribute("Iron", "1g")),
new XElement("TotalFat", "16g",
new XAttribute("SaturatedFat", "7g"),
new XAttribute("TransFat", "9g"))
) );
// Add new type of Icecream to the existing XML
ClassicIcecreams.Add(
new XElement("Icecream",
new XElement("Name", "Banana Split Icecream"),
new XElement("Cholesterol", "58mg"),
new XElement("TotalCarbohydrates", "24g"),
new XElement("Protein", "6g",
new XAttribute("VitaminA", "2g"),
new XAttribute("Iron", "1g")),
new XElement("TotalFat", "13g",
new XAttribute("SaturatedFat", "7g"),
new XAttribute("TransFat", "6g"))
));

After creating the XML element, save it as an XML file under a directory using the code below:

// Save that as an XML file
ClassicIcecreams.Save(@"C:ClassicIcecreamsList.xml");

Check if the text file we are going to create already exists in the directory. If the file does not exist, we will proceed with constructing the query and fetching rows. Then we can create a text file and write into it.

// Text file to store the xml content
string path = @"c:ClassicIcecreamsList.txt";
if (!File.Exists(path))
{
// Load the XML file into an XElement
XElement LoadClassicIcecreamsList = XElement.Load(@"C:ClassicIcecreamsList.xml");

Using LINQ, query the XML element to fetch the records one-by-one. On fetching the records, we have to separate the fields or values using the comma delimiter so that we can identify fields next time we read it. To convert the XML element into delimited strings, we have used the formatting of String object. Pass all different element values as parameters to the format method.

Note that the following code fetches even the attribute values of the XML elements, and passes that as strings to the Format method. The last line in the Select statement uses the Environment.NewLine method to include a line break at the end of each record. The aggregate operator is used to append all values of the child elements and then format it with a comma delimiter.

// Using Linq query the XElement to fetch records with the comma delimiter string Ice = (from el in LoadClassicIcecreamsList.
Elements("Icecream")
select String.Format("{0}, {1}, {2}, {3}, {4}, {5}, {6} {7}",
(string)el.Element("Name"),
(string)el.Element("Cholesterol"),
(string)el.Element("TotalCarbohydrates"),
(string)el.Element("Protein").Attribute("VitaminA"),
(string)el.Element("Protein").Attribute("Iron"),
(string)el.Element("TotalFat").Attribute("SaturatedFat"),
(string)el.Element("TotalFat").Attribute("TransFat"),
Environment.NewLine
)
).Aggregate(
new StringBuilder(),
(sb, s) => sb.Append(s),
sb => sb.ToString()
);

Now we have all the XML element values as delimited strings. Using the StreamWriter, we can write the string into a text file.

We can also create the CSV file using the File.WriteAllText method by passing the string containing the text.

// Add all the records stored in the string Ice to the text file
using (StreamWriter sw = File.CreateText(path))
{ sw.WriteLine(Ice); }
// Create a csv file and write all the records stored in the string Ice
File.WriteAllText(@"C:Icecreams.csv", Ice);
}

After writing the string into the text file, the text file will contain the following text:

Chocolate Fudge Icecream, 50mg, 35g, 3g, 1g, 9g, 11g
Vanilla Icecream, 65mg, 26g, 1g, 1g, 7g, 9g
Banana Split Icecream, 58mg, 24g, 2g, 1g, 7g, 6g

Reading from CSV Files

We have seen howto create text and CSV files and write XML elements into it. Now we will see how to get details from the CSV file and construct an XML from it. Using the functional construction of XElement, we can easily build the XML from the CSV data source. The only thing is, we should know the field names for the values we have in the CSV file. The sample code below explains how to read from a file and

construct the XML. The first thing is to load the CSV file into an array of strings. Using the query, fetch records from the strings. On fetching each record, we have to split the strings according to the comma delimiter. So the Split method is used for spliting the string into a field array. Then from the field array, we can take individual fields using the array index and assign them to the corresponding XML element.

// Read all the details from CSV to string array
string[] source = File.ReadAllLines(@"C:Icecreams.csv");
// Using Query get all the field values and assign that to elements
XElement ice = new XElement("Icecreams",
from str in source
let fields = str.Split(‘,')
select new XElement("Icecream",
new XElement("Name", fields[0]),
new XElement("Cholesterol", fields[1]),
new XElement("TotalCarbohydrates", fields[2]),
new XElement("Protein",
new XAttribute("VitaminA", fields[3]),
new XAttribute("Iron", fields[4])),
new XElement("TotalFat",
new XAttribute("SaturatedFat", fields[5]),
new XAttribute("TransFat", fields[6]))
)
);
// Save the XML tree as xml file
ice.Save(@"c:icecreamxml.xml");

Eexecuting this code will create the icecreamxml.xml file. The contents would be as follows:

<?xml version="1.0" encoding="utf-8"?>
<Icecreams>
<Icecream>
<Name>Chocolate Fudge Icecream</Name>
<Cholesterol> 50mg</Cholesterol>
<TotalCarbohydrates> 35g</TotalCarbohydrates>
<Protein VitaminA=" 3g" Iron=" 1g" />
<TotalFat SaturatedFat=" 9g" TransFat=" 11g " />
</Icecream>
<Icecream>
<Name>Vanilla Icecream</Name>
<Cholesterol> 65mg</Cholesterol>
<TotalCarbohydrates> 26g</TotalCarbohydrates>
<Protein VitaminA=" 1g" Iron=" 1g" />
<TotalFat SaturatedFat=" 7g" TransFat=" 9g " />
</Icecream>
<Icecream>
<Name>Banana Split Icecream</Name>
<Cholesterol> 58mg</Cholesterol>
<TotalCarbohydrates> 24g</TotalCarbohydrates>
<Protein VitaminA=" 2g" Iron=" 1g" />
<TotalFat SaturatedFat=" 7g" TransFat=" 6g " />
</Icecream>
</Icecreams>

LINQ to XML Events

LINQ to XML is mainly used for manipulating and navigating through XML tree. There are chances that many queries may try to access the same XML tree. In this situation, we always like to be notified about changes that happens to the XML data on which our query depends. LINQ provides the feature of associating events to the XML. There are two types of events that can be set to the XML when there is a change to the XML tree.

Events can be added to any instance of an XObject. The event handler will receive the events for modifications to that XObject and any of its descendants. The following events are raised when the XML tree is modified.

  • Changing—occurs just before changing the XObject or any of its descendants.

  • Changed—occurs when the XObject or any of its descendants have changed.

There are different objects and types used when we work with events. These types are used for getting the event type, information about the change, and the information about the object that's affected by the change.

  • XObjectChange, provides the event type when an event is raised for an XObject.

  • XObjectChangeEventArgs, provides data for the changing and changed events.

  • XObjectChangeEventHandler, represents the method that will handle the events.

Following is the ClassicIcecreams XML element which contains information about an ice-cream type.

// Create a sample XML
XElement ClassicIcecreams =
new XElement("Icecreams",
new XElement("Icecream",
new XElement("Name", "Chocolate Fudge Icecream"),
new XElement("Ingredients", "cream, milk, sugar, corn syrup, cellulose gum..."),
new XElement("Cholesterol", "50mg")
)
);

For this XML tree, we will associate the changing and changed event so that we know about any change when it happens to the XML tree.

Create the new XObjectChangeEventHandler and associate it with the changing event of the XML element. This handler has a delegate which takes two parameters—one is of type object, and the other is of type XObjectChangeEventArgs. So if any change occurs to the ClassicIcecreams XML tree, this changing event fires just before the actual change. This event will display information like the sender's name and the operation that is making the object change. The type of the operation is taken from the XObjetChangeEventArgs argument.

// Create a Changing event for the ClassicIcecreams
element
// Show message with details that will be changing
ClassicIcecreams.Changing += new XObjectChangeEventHandler(
delegate(object objSender, XObjectChangeEventArgs args)
{
XElement eleSend = (XElement)objSender;
MessageBox.Show("XML is Changing " + " 
 " +
" Sender: " + eleSend.Name.LocalName +
" Operation: " + args.ObjectChange.ToString(),, "Changing Event");
}
);

Create another new XObjectChangeEventHandler with the similar parameters and types as we used for the previous example. This event handler is for handling the changed event of the XML tree. Assign this event to the Changed event property of the XML. This event will be fired after changing the XML tree. Here, also, we are displaying the sender's name and the change operation that caused the event to fire.

// Create a Changed event for the ClassicIcecreams element
// Show message with the details that got changed
ClassicIcecreams.Changed += new XObjectChangeEventHandler(
delegate(object objSend, XObjectChangeEventArgs args)
{
XElement eleSend = (XElement)objSend;
MessageBox.Show(" XML Changed " + "
 " +
" Sender: " + eleSend.Name.LocalName +
" Change: " + args.ObjectChange.ToString(), "Changed Event");
}
);

Now create a new XML element which has the same number of elements and attributes. We will use this new element to raise events on the original XML element.

// Create a new XML element
XElement NewIcecream = new XElement("Icecream1",
new XElement("Name", "Vanilla Icecream"),
new XElement("Ingredients", "vanilla extract, guar gum, cream, nonfat milk, sugar, locust bean gum, carrageenan, annatto color..."),
new XElement("Cholesterol", "65mg")
);

Now add the new element to the existing ClassicIcecream element so the event gets fired.

// Add the new element to the ClassicIcecreams so that
the events get fired
ClassicIcecreams.Add(NewIcecream);

At once, when we try to add new elements to the existing ClassicIcecream element, the changing event fires just before the change happens. The raised event will show a message similar to the one below:

// Remove an element from the ClassicIcecreams element so
that the events get fired
ClassicIcecreams.Element("Icecream").Remove();
}

XML Literals and Embedded Expressions in Visual Basic

Visual Basic supports XML to be added to the code by XML literals. This makes it easier to create XML elements, documents and fragments as we have the code and XML together without any additional dependency. Visual Basic compiles the XML Literals to LINQ to XML Objects. LINQ to XML provides a simple object model by which we can manipulate the XML data.

The following code shows a sample of XML literals added to the Visual Basic code which gives a LINQ to XML XElement object. We just have to type or copy the XML directly to the code section. An XML literal does not require a line continuation character. This helps us copy the XML into code without any changes or updates to the XML. If we add the line continuation character to the XML, the compiler will treat the line continuation character as part of the XML. In this example, we have not used any line continuation character in the XML literal.

Dim raisinIcecream As XElement = _
<Icecream>
<Name>Rum Raisin Ice Cream</Name>
<Ingredients>Rum, guar gum, milk, alomnds, sugar,
raisins, honey, chocolate, annatto color...</Ingredients>
<Cholesterol>49mg</Cholesterol>
<TotalCarbohydrates>28g</TotalCarbohydrates>
<Protein VitaminA="2g" Iron="4g">6g</Protein>
<TotalFat SaturatedFat="5g" TransFat="3g">8g</TotalFat>
</Icecream>

Visual Basic also provides an additional feature of adding expressions to XML literals. This helps us to add dynamic content to the XML literal. For example, the following XML literal uses embedded expressions to crate the XML element from the parameter values passed to the method. When we add expression to the literal, we also get the IntelliSense help from Visual Studio to easily select the elements.

XML Literals and Embedded Expressions in Visual Basic

Following is the sample of an XML, created by passing the values to CallIcecreamsEmbedded, which returns the XElement.

XML Literals and Embedded Expressions in Visual Basic

The expressions value can be a simple text, or it can be a query. The query can be used to build the XML and the result can be an XML literal. The following code shows a sample of an XML literal which uses the query in expressions to build XML by fetching details from the Icecreams XML.

Dim Icecreams1 As XElement = _
<Icecreams>
<%= From c In Icecreams.Elements("Icecream") _
Select New XElement("Icecream", _
c.Element("Name").Value.ToUpper()) %>
</Icecreams>

Summary

In this chapter, we saw information and examples on programming with LINQ to XML. We have seen the advantages of Functional Construction in constructing the XML tree and navigating through the XML tree. We also manipulated the XML data in the XML tree using XElement and XAttribute object properties. We saw some examples for querying the XML using LINQ provided query operators. We also learned importing and exporting data from different data sources like dictionaries, databases objects, and CSV files. Lastly we saw different events that can be fired when modifying the XML tree. With all these features provided by LINQ to XML, we can easily manipulate XML data through .NET code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.81.14