Serengeti logo BLACK white bg w slogan
Menu

Handling XML data in .NET

Igor Marković, Software Development Consultant
12.11.2021.

XML is a widely used markup language in various environments. It is easily human and machine readable. Although it has recently been replaced by JSON in some usages, it still appears very frequently. In this post we will make an overview of automated XML data handling in .NET environment, using C# programming language.

Overview

The most commonly used classes are contained in System.Xml.Linq namespace. Here is a class diagram with the required classes:

class diagram

The base abstract class is XObject. XAttribute is derived directly from XObject class and it corresponds to XML attributes. XDocument and XElement are derived from two other intermediate abstract classes: XNode and XContainer. XDocument and XElement correspond to the whole XML document and single XML element, respectively.

The concrete classes XAttribute, XElement, and XDocument, are sufficient for basic data manipulations. There are several other classes in the namespace, but their usage is not required for basic operations.

Reading data

For the needs of this post, let’s create a sample xml file called test1.xml with the following content:

<MyXDocument>

      <MyXElement1 Attribute1="Value 1.1">

            <MyXElement2>Element 2.1</MyXElement2>

      </MyXElement1>

      <MyXElement1 Attribute1="Value 1.2">

            <MyXElement2>Element 2.2</MyXElement2>

      </MyXElement1>

</MyXDocument>

The document can be opened using XDocument.Load method:

string filePath = @"<path_to_the_file>";

XDocument xDoc = XDocument.Load(filePath);

Once the document is opened, it can be queried using LINQ like any other query-able data collection. In XDocument and XElement classes we can use two basic methods for querying:

  • () – returns a collection of descendant elements, i.e., elements any level below current element’s level
  • Elements () – returns a collection of child elements, i.e., elements only one level below current element’s level

Additionally, XDocument class contains the Root property, which corresponds to the root element of the XML document.

Therefore, in this case,  Descendants () method returns the following collection:

var descendants = xDoc.Descendants();

sh02

The Elements() method of the Root element returns the following collection:

var elements = xDoc.Root.Elements();

sh03

Additionally, there is an overload of these methods with string parameter which filters the collection by the specified name.

Now let’s make some LINQ queries.

Let’s retrieve a collection of elements whose value of sub-element MyXElemet2 is “Element 2.1”. The criteria would be the following:

var qry1 = xDoc.Root.Elements().Where(x => x.Element("MyXElement2").Value == "Element 2.1");

The result:

sh05

In another example, we will search by attribute value. For retrieving a collection of elements where value of the attribute “Attribute1” is “Value 1.2”, the criteria would be:

var qry2 = xDoc.Root.Elements().Where(x => x.Attribute("Attribute1").Value == "Value 1.2");

Saving data

Things can also go the other way round – generating an xml document. The document in this example can be generated in C# in the following way:

//create new XDocument object

XDocument xDoc = new XDocument();

//create the root element

XElement rootElem = new XElement("MyXDocument");

//create the first element with the attribute and sub-element

XElement elem1_1 = new XElement("MyXElement1");

elem1_1.Add(new XAttribute("Attribute1", "Value 1.1"));

XElement elem2_1 = new XElement("MyXElement2", "Element 2.1");

elem1_1.Add(elem2_1);

//create the second element with the attribute and sub-element

XElement elem1_2 = new XElement("MyXElement1");

elem1_2.Add(new XAttribute("Attribute1", "Value 1.2"));

XElement elem2_2 = new XElement("MyXElement2", "Element 2.1");

elem1_2.Add(elem2_2);

//add the created elements to the root element

rootElem.Add(elem1_1);

rootElem.Add(elem1_2);

//add the root element to the XDocument

xDoc.Add(rootElem);

//save the generated document to a file

string filePath = @"<path>";

System.IO.File.WriteAllText(filePath, xDoc.ToString());

This code generates an XML file with the content identical as in the example.

XML serialization

Another aspect of XML data handling is data serialization of .NET classes. .NET framework contains the mechanism to generate XML from a class, as well as the other way round – parse XML and generated the corresponding object.

Let’s make a simple example class:

public class MyClass1

{

       public int ID { get; set; }

       public string Name { get; set; }

       public string Address { get; set; }

}

An object of type MyClass1:

MyClass1 obj1 = new MyClass1 { ID = 1, Name = "Obj 1", Address = "Main Street 1" };

In .NET Framework there is no out-of-the-box method for generating an XML element based on a .NET object. Therefore, a simple helper method can be used:

public static XElement ToXElement<T>(T obj)

{

  using (var memoryStream = new MemoryStream())

  {

    using (TextWriter streamWriter = new StreamWriter(memoryStream))

    {

      var xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T));

      xmlSerializer.Serialize(streamWriter, obj);

      return XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));

    }

  }

}

This generic method accepts and object of specified type and returns its XML representation. In this case, the respective XML of the object in the example looks like this:

<MyClass1 xmlns:xsd="http://www.w3.org/2001/XMLSchema"                                       

 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <ID>1</ID>

  <Name>Obj 1</Name>

  <Address>Main Street 1</Address>

</MyClass1>

In case some properties have to be omitted from XML serialization, it can be achieved by using XmlIgnore Attribute:

[System.Xml.Serialization.XmlIgnore]

public string Address { get; set; }

In this case the generated XML does not contain the “Address” element:

<MyClass1 xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <ID>1</ID>

  <Name>Obj 1</Name>

</MyClass1>

For the other direction, parsing an XML element to .NET object, the following method can be used:

public static T FromXElement<T>(XElement xElement)

{

var xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T));

return (T)xmlSerializer.Deserialize(xElement.CreateReader());

}

Let’s make an example XML file:

<MyClass1>

  <ID>2</ID>

  <Name>Obj 2</Name>

</MyClass1>

Deserialization works also without the attributes “xsd” and “xsi” which are automatically added during serialization.

Deserialization in C# code:

string filePath = @"<path>";

XDocument xDoc = XDocument.Load(filePath);

MyClass1 obj = XmlHelper.FromXElement<MyClass1>(xDoc.Root);

The result:

sh06

Conclusion

.NET framework provides pretty good mechanisms for handling XML data. You can easily parse and query XML documents using LINQ methods, like any other data collection. Also, XML serialization provides a simple way to save some data from an application to XML.

Let's do business

The project was co-financed by the European Union from the European Regional Development Fund. The content of the site is the sole responsibility of Serengeti ltd.
cross