ninja star Getting Started with LINQ to XML

by Michael Ceranski, posted on February 21 2010

man-pulling-hair-out I recently announced the WeBlog project. WeBlog is a blogging platform which will support multiple data providers. Out of the box I plan on offering SQL Server and XML support. Most people like the XML option because it drastically reduces web hosting costs. The only problem with XML is that it can be painful to work with. In general, XML makes me want to pull my hair out!

When building a blog you have a few basic entities that you need to deal with. Most typical blogs have posts, categories, tags, users and roles. Therefore I made an XML file to represent each of these items. However, for this tutorial I will focus on parsing the XML for categories. For a point of reference here is the XML structure that I am using to store category information:

 

<?xml version="1.0" encoding="utf-8"?>
<categories>
  <category id="19770e74-9ec9-4cde-b2ab-e5051aaaf348" description="Posts about my adventures with WeBlog" 
     parent="" name="WeBlog">
    <posts>
      <post id="0e05a782-7440-46e9-8fc4-e33fd51685e9" />
    </posts>
  </category>
  <category id="c223353c-1aef-4a46-afd1-cb61ab1a792d" description="" parent="" name="Tech">
    <posts>
      <post id="1aeaa3a2-6dfb-4a57-a633-0c1597e162ff" />
    </posts>
  </category>
<categories>

From the XML above you can deduce that each category has a ID, Name, Description and Parent. In addition, the category has related posts.

Since my application supports multiple data providers I first created an interface which all providers must adhere to. The methods defined in the interface return common objects. This allows me to abstract from the underlying data store. For example, here is the CategoryModel class which is really just an object representation of the XML:

public class CategoryModel {
    public Guid ID { get; set; }
    public Guid? Parent { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public int PostCount { get; set; }
}

Selecting Data

The job of the XML data provider is to create a list of CategoryModel objects by reading the XML. Luckily, this process is relatively simple with LINQ to XML. First, we create an XDocument object and then loop through each category node. For each category we can read the attributes and elements to populate a CategoryModel object which can be added to a generic list:

 public List<CategoryModel> FindCategories()
 {      
     List<CategoryModel> categories = new List<CategoryModel>();
     XDocument xmlDoc = GetCategoryXML();
     var query = from category in xmlDoc.Descendants("category")
                 select new CategoryModel
                 {
                     ID = Guid.Parse(category.Attribute("id").Value),
                     Name = category.Attribute("name").Value,
                     Parent = (Guid?)(category.Element("parent") == null ? (Guid?)null :
                              Guid.Parse(category.Element("parent").Value)),            
                     Description = category.Attribute("description").Value,
                             PostCount = category.Descendants("post").Count()   
                 };
     categories = query.ToList();            
     return categories;
 }

Deleting Data

To delete a category we can use a similar approach. Again we need to create an XDocument and find the correct node by matching the id attribute of the category node. Once, we have the proper node selected we can call Remove() and then save the document:

public void DeleteCategory( Guid id )
{            
    XDocument xmlDoc = GetCategoryXML();
    var category = from x in xmlDoc.Descendants("category")
                where
                    x.Attribute("id").Value == id.ToString()
                select x;
    category.Remove();
    xmlDoc.Save(GetCategoryXMLFilename());   
}

Creating Data

Creating data is also a simple task. You create the category element and then assign the attributes and child elements. The syntax is a little bit different than the select and delete operations but overall it is fairly straightforward:

public void InsertCategory(CategoryModel source)
{
    XDocument xmlDoc = GetCategoryXML();
    XElement category = new XElement( "category",
                            new XAttribute( "id", source.ID.ToString() ),                                    
                            new XAttribute("description", source.Description),
                            new XAttribute( "parent", source.Parent == null ? "" : source.Parent.ToString() ),
                            new XAttribute("name", source.Name),
                        new XElement( "posts" ));
    xmlDoc.Element("categories").Add( category );            
    xmlDoc.Save(GetCategoryXMLFilename());
}

Updating Data

In order to update data you need to first find the correct element in the XML document. Once you have the element selected you can update the attributes and child values by using assignment operators. Once you make the updates you can save the document :

public void UpdateCategory(CategoryModel source)
{
    XDocument xmlDoc = GetCategoryXML();
    var category = ( from x in xmlDoc.Descendants("category")
                   where
                      x.Attribute("id").Value.Equals(source.ID.ToString(), StringComparison.CurrentCultureIgnoreCase)
                   select
                      x ).Single();
    category.Attribute("description").Value = source.Description;
    category.Attribute("name").Value = source.Name;
    xmlDoc.Save(GetCategoryXMLFilename());
}

As I mentioned before, XML is not my favorite format but LINQ to XML at least makes the process bearable. Hopefully this brief introduction will make you feel better about XML parsing. Maybe it will even save you from pulling your hair out!

Tags: ,
blog comments powered by Disqus

About the author

MikeMichael Ceranski is a developer specializing in the .NET stack. I have spent time as a DBA, Web Developer and even a network engineer. Up til now most of my career has revolved around the .NET stack but I have recently taken an interest in microcontrollers which has forced me to get acquainted with lower level languages such as C, and C++.

View my resume

Sponsors