Reading and Writing data using custom methods and the System.Xml Namespace

On a previous post dealing with Xml Serialization I mentioned that one of the ways you could achieve this was by customizing the read and write methods. I’m going to bypass the wrapping the code into Read/Write methods and go straight to the code:

 

Let’s start with this simple xml file:

 

<Items>

<Item>MyItem 1</Item>

<Item>MyItem 2</Item>

<Item>MyItem 3</Item>

</Items>

 

If the structure of your xml contains a root node (Items from our example) you need to declare a root node and select that node from the XmlDocument instance which will return an XmlNode and then select all nodes from that root node

 

String xmlName = “temp.xml”;

String rootName = “Items”;

String itemName = “Item”;

XmlDocument xmlDoc = new XmlDocument();

xmlDoc.Load(xmlName);

XmlNode root = xmlDoc.SelectSingleNode(rootName);

XmlNodeList nodeList = root.SelectNodes(itemName);

 

The only difference if you don’t have a root is to skip the call to SelectSingleNode and call SelectNodes directly from the XmlDocument instance.

Iterating a list of nodes

 

After getting an XmlList object iterating that list is simple, you can use a foreach statement to go through each one of them:

 

foreach (XmlNode item in nodeList)

{

// Do some stuff

}

 

If you have a more complicated xml file with nested nodes you just have to nest the iterations and use the childNodes property of XmlNode:

 

foreach (XmlNode child in item.ChildNodes)

{

// Do some stuff

}

 

XmlNode Properties

 

The full list of properties is available on MSDN () so I’m just going to mention a couple that are pertinent to this article:

 

Name – Returns the name of the node (ie: “Item”)

InnerText – Returns the content of the node (ie: “MyItem 1”). If there’s nested tags inside the node it will return their value (more on this later).

AttributeCollection – Collection of attributes (each attribute is considered a node).

 

XmlAttributes

 

Let’s say we got a slightly more complicated Xml file:

 

<Items>

<Item Type=”Type1”>

<Name>My Item 1</Name>

<Description>No description available</Description>

</Item>

<Item Type=”Type2”>

<Name>My Item 2</Name>

<Description>Item defined on summer catalog, page 2</Description>

</Item>

</Items>

 

The values declared and defined inside a tab are called attributes. In the example above all Item tags have an attribute called Type. To access these you need to access the AttributeCollection property of the XmlNode and then the specific element you want:

 

String attributeName = “Type”;

String attributeValue = String.Empty;

XmlNode attribute = item.Attributes[attributeName];

if (!ReferenceEquals(attribute, null))

{

attributeValue = attribute.Value;

}

 

Nested Tags

 

Take the same example from the previous section but let’s say that instead of having the page and the catalog hardcoded on item 2 you wanted to just put the ID’s in the xml and have a method in your code resolve this (by simply looking the id’s up on a table or something more complicated as a database search. The xml file would look like this:

 

<Items>

<Item Type=”Type2”>

<Name>My Item 2</Name>

<Description>Item defined on <ref>A23</ref> catalog, page <ref>B71<ref></Description>

</Item>

</Items>

 

For simplicity purposes lets presume there’s a method call LookupID which resolves the ID’s to their specific values. The code to get full description would look like this:

 

String fullDescription = String.Empty;

XmlNode node = …; //Get the XmlNode from the current iteration

foreach (XmlNode child in node)

{

       if (child.Name.ToLower() == "ref")

              child.InnerText = LoookupID(child.InnerText);

}

fullDescription = node.InnerText

return (node.InnerText);

The InnerText property will strip the tags from the node and just return their values along with the rest of the string. For example if you just got node.InnerText without resolving the ID’s you would get “Item defined on A23 catalog, page B71”.

 

Writing Data

 

Let’s say that for the example above you have a corresponding Item class which has a Name and Description property:

 

class Item

{

       public String Name { get ; set; }

       public String Description { get ; set; }

       public Item() {}

}

 

After populating a list of Item you can use a combination of XmlTextWriter and XmlDocument to write your document. Basically you’ll be using XmlTextWriter to create the xml document headers:

 

String filename = …

String comment = …

String rootName =

XmlTextWriter xmlTextWriter = new XmlTextWriter(filename, System.Text.Encoding.Unicode);

try

{

xmlTextWriter.Formatting = Formatting.Indented;

       xmlTextWriter.WriteStartDocument(false);

       xmlTextWriter.WriteComment(comment);

xmlTextWriter.WriteStartElement(rootName);

xmlTextWriter.WriteEndElement();

}

finally

{

if (!ReferenceEquals(xmlTextWriter, null))

{

xmlTextWriter.Flush();

xmlTextWriter.Close();

}

}

 

A couple of quick note on comments, first the obvious, they are optional so you don’t need to call WriteComment. The other note being, you can also write as many of them as you want so you can have multiple calls to WriteComment.

 

After a valid xml document exists you can create an XmlDocument instance and iterate through the items in the list to add each of them. For each item we’re going to create a XmlNode on which we’re going to append multiple XmlElements (one for each property we want to add) by calling the AppendChild method of the node. After that node is populated we’re going to add it to the root node and after the iteration is done we’ll call Save on the XmlDocument to have the changes written to the file:

 

String filename = …

List<Item> items = …

XmlDocument  xmlDoc = new XmlDocument();

xmlDoc.Load(filename);

XmlNode root = xmlDoc.DocumentElement;

                    

foreach (Item item in items)

{

XmlNode itemNode = xmlDoc.CreateElement(elementName);

XmlElement name = xmlDoc.CreateElement(propertyName_Name);

name.InnerText = item.Name;

itemNode.AppendChild(name);

XmlElement description = xmlDoc.CreateElement(propertyName_Description);

description.InnerText = item.Description;

itemNode.AppendChild(description);

root.AppendChild(itemNode);

}

xmlDoc.Save(filename);