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.
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.
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.
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:
We will see details of the important classes used, to work with the elements and attributes in XML documents, in the following sections.
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"))
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"))
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();
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>
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.
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.
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.
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 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:
XElement(XName name)
—creates an XML element with the name specified in the XName
parameter.
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.
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.
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>
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.
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>");
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 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.
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
.
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.
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.
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";
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();
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");
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.
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 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.
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.
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.
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.
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.
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.
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>
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
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
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 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(); }
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.
Following is the sample of an XML, created by passing the values to CallIcecreamsEmbedded
, which returns the XElement
.
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>
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.
3.16.81.14