Getting Started with LINQ to XML

When working with XML and the .NET Framework you can take advantage of LINQ’s syntax to perform CRUD (Create, Read, Update, Delete) operations on XML files. In this example, we will be looking at LINQ to XML and seeing how we can read XML files, its data and add new elements. In addition to understanding how to use LINQ to XML I will also show you how to generate an XSD schema from our XML, auto generate a C# class based on its structure and serialize our XML data into this class so we can use the XML data in C# objects.

Using LINQ to XML to read data

Let's have a look at our example XML doc.

<?xml version="1.0" encoding="UTF-8"?>
<cars>
  <car>
    <make>Lexus</make>
    <model trim="450h" fuelType="Hybrid" Transmission="auto" year="2006" doors="4">GS</model>
  </car>
  <car>
    <make>Mercedes-Benz</make>
    <model trim="SL55" fuelType="Petrol" Transmission="auto" year="2006" doors="2">SL</model>
  </car>
  <car>
    <make>Honda</make>
    <model trim="TypeR" fuelType="Petrol" Transmission="manual" year="2008" doors="2">Civic</model>
  </car>
  <car>
    <make>BMW</make>
    <model trim="Convertible SMG" fuelType="Petrol" Transmission="auto" year="2002" doors="2">M3</model>
  </car>
</cars>

It’s a simple document containing 4 car elements. Each element of type car has 2 child elements make and model. The model element has 5 attributes.

I have uploaded the example XML document to the IntermittentBug server, you can access it from this URL -> https://www.intermittentbug.com/ExampleFiles/cars.xml

To start let's look at how we can first download this XML document using the WebClient Class and create an object of type XDocument from this XML file.

string xmlStr = String.Empty;
using (WebClient wc = new WebClient())
{
    xmlStr = wc.DownloadString(URL);
}

XDocument xdoc = XDocument.Parse(xmlStr);
return xdoc;

Ok, let's create a method using this code so we can reuse this in the following examples.

private string _URL = "https://www.intermittentbug.com/ExampleFiles/cars.xml";

private XDocument DownloadXMLtoXDocument(string URL)
{
    string xmlStr = String.Empty;
    using (WebClient wc = new WebClient())
    {
        xmlStr = wc.DownloadString(URL);
    }

    XDocument xdoc = XDocument.Parse(xmlStr);
    return xdoc;
}

[TestMethod]
public void LoadXMLFromURL()
{
    XDocument xdoc = DownloadXMLtoXDocument(_URL);

    Assert.IsTrue(!String.IsNullOrEmpty(xdoc.ToString()));
}

To begin let’s iterate through our XML document and get the car elements as a list. The core object we will be using is the XElement object. It's also important to understand that you can code LINQ in 2 different ways. The SQL way as I refer to it as its very similar to SQL syntax or the Lamda way with its more functional coding style. Initially when I started it use LINQ I prefered the SQL style as its simpler to understand, but as time went on I now prefer the functional style, so much so that I rarely use SQL style in my code today.

[TestMethod]
public void TestGetCars_SQLStyle()
{
    // get XML doc as XDocument
    XDocument xdoc = DownloadXMLtoXDocument(_URL);

    // SQL Style
    var cars = from car in xdoc.Descendants("car")
                select car;

    // loop through the car elements and check that the trim arributre is not null or empty
    foreach (XElement car in cars)
    {
        Assert.IsTrue(!String.IsNullOrEmpty(car.Element("model").Attribute("trim").Value));
    }
}
[TestMethod]
public void TestGetCars_LamdaStyle()
{
    // get XML doc as XDocument
    XDocument xdoc = DownloadXMLtoXDocument(_URL);

    // lambda style
    var cars = xdoc.Descendants("car").ToList();

    // loop through the car elements and check that the trim arributre is not null or empty
    foreach (XElement car in cars)
    {
        Assert.IsTrue(!String.IsNullOrEmpty(car.Element("model").Attribute("trim").Value));
    }
}

With either of these methods the result is the same, it's just a different style to get the car elements.

For this example, let's have a look at how we can get all the make strings from the XML.

[TestMethod]
public void TestGetMakes()
{
    // get XML doc as XDocument
    XDocument xdoc = DownloadXMLtoXDocument(_URL);

    var makes = from car in xdoc.Descendants("car")
                select car.Element("make").Value;

    foreach (string make in makes)
    {
        Assert.IsTrue(!String.IsNullOrEmpty(make));
    }
}

Add Elements using LINQ to XML

Let's now look at a more complex example where this time we will add a new car element to the XML. Using LINQ to XML you can create new elements and then add them where you want in your XML document. We will create a new car element using the XElement object. We will then create 2 more XElement objects for the make and model. The model is more complex as it requires 5 attributes.

I want to be able to add this new car element as a sibling to the last car element.

To do this we can either use XPath which is an expression language used to traverse XML documents or using the Lamda Style to get the last car element. I will show you both.

/// <summary>
/// Shows how to add a new car element with LINQ to XML
/// </summary>
[TestMethod]
public void TestAddNewCarElement()
{
    // get XML doc as XDocument
    XDocument xdoc = DownloadXMLtoXDocument(_URL);

    XElement CarElement = new XElement("car");
    XElement MakeElement = new XElement("make", "Nissan");
    XElement ModelElement = new XElement("model", "350Z");

    List<XAttribute> AttributeList = new List<XAttribute>();
    AttributeList.Add(new XAttribute("trim", "Nismo"));
    AttributeList.Add(new XAttribute("fuelType", "Petrol"));
    AttributeList.Add(new XAttribute("Transmission", "auto"));
    AttributeList.Add(new XAttribute("year", "2005"));
    AttributeList.Add(new XAttribute("doors", "2"));

    // add the attributes
    foreach (XAttribute Attribute in AttributeList)
    {
        ModelElement.Add(Attribute);
    }

    CarElement.Add(MakeElement);
    CarElement.Add(ModelElement);

    // Xpath to get last car node
    //xdoc.XPathSelectElement("./cars/car[last()]").AddAfterSelf(CarElement);

    //LinqToXML to get last car node
    xdoc.Element("cars").Add(CarElement);

    var XMLStr = xdoc.ToString();
}

If you want to use Xpath you can swap the line 34 with the code below.

// Xpath to get last car node
xdoc.XPathSelectElement("./cars/car[last()]").AddAfterSelf(CarElement);

Creating a XML Schema and Auto Generating a Class

In this example, I will show you how to auto generate a C# class from our XML document. We will then use this class to serialize the XML to a C# object.

To start we need to generate a XML Schema. A Schema document provides validation for your XML with regards to data and document structure. This is critical for any systems that accept XML as a data format.

The easiest way to generate a schema for your XML is to open an XML document in Visual Studio and then using the XML toolbar, select "Create Schema". This will auto generate the schema for you.

If you are not using Visual studio you can use the XSD generator tool from http://www.freeformatter.com

Simply copy the XML and click generate XSD. Save the output as in a new file called cars.xsd.

I have created a schema document that is available to download from IntermittentBug. You can access it from this URL -> https://www.intermittentbug.com/ExampleFiles/cars.xsd

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="cars">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="car">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="make" type="xs:string" />
              <xs:element name="model">
                <xs:complexType>
                  <xs:simpleContent>
                    <xs:extension base="xs:string">
                      <xs:attribute name="trim" type="xs:string" use="required" />
                      <xs:attribute name="fuelType" type="xs:string" use="required" />
                      <xs:attribute name="Transmission" type="xs:string" use="required" />
                      <xs:attribute name="year" type="xs:unsignedShort" use="required" />
                      <xs:attribute name="doors" type="xs:unsignedByte" use="required" />
                    </xs:extension>
                  </xs:simpleContent>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Now we need to generate the class we can use to for our XML document. To generate it we can use the XSD.exe tool available in Microsoft .NET. This tool needs to be run from the command line to execute. Follow the steps below to generate the class.

  1. Open a CMD command line window. Press Windows key and type “cmd” open in admin mode if necessary.
  2. Type "cls" to clear the window of text.
  3. Now type "cd C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools"
  4. Copy your XSD file to the root of your C:\ drive.
  5. In the command window enter "xsd C:\cars.xsd /classes"
  6. This will generate the class cars.cs

The code that is generated is verbose and contains many C# class attributes, this is a clear sign it is autogenerated code.

Now we have our car class we can serialize our XML string into a C# object. Using the XmlSerializer setting in the constructor as the type of our generated class. We can use the XmlReader class to easily convert our XML into an object. See the code below.

[TestMethod]
public void TestXMLSeralization()
{
    // get XML doc as XDocument
    XDocument xdoc = DownloadXMLtoXDocument(_URL);
    cars CarsXMLObj = new cars();

    XmlSerializer ser = new XmlSerializer(typeof(cars));

    using (XmlReader reader = XmlReader.Create(new StringReader(xdoc.ToString())))
    {
        CarsXMLObj = (cars)ser.Deserialize(reader);
    }

    foreach (var car in CarsXMLObj.car)
    {
        Assert.IsTrue(!String.IsNullOrEmpty(car.make));
    }
}

So, there we have it, a simple but useful introduction to using LINQ to XML and working with XML in .NET. Happy Coding! 


JGilmartin Profile Image

JGilmartin

Technical Architect at Pinewood Technologies

Rating: 2890

C# Expert

Offline


Tutorial Statistics
  • Views: 2651
  • Comments: 0
  • Author: JGilmartin (2890)
  • Date: 25/2/2017 22:21
Tags
C# LINQ XML

© 2016 - 2018 - IntermittentBug