Every now and again (okay, frequently), I come across something that really makes me feel like a infant developer. I was working on a problem in an application and am planning to use an XML file to manage some custom config-ish things. Now I've done this many times before and was dreading my walk down the XPath trail of tears (yeah, I don't like XPath, if you do, great; I don't) and decided to do some searching for a better way to work with XML. After all, if Microsoft is so gung-ho about it, there *has* to be a better way... it turns out that there is...

(More after the jump)



Okay, so the correct (maybe not correct, but at least REALLY GOOD) way to work with XML in your C# application  is to serialize the file in & out of an object. If you know how to do this already, awesome; you can stop reading now (go ahead and click on that ad up there and help support this blog, though). If you don't know how, hold on because it's going to be a fast ride...

So the first thing you should do is create a template of your XML file. Be sure to include every element and attribute that you want available. If you want multiple instances of an element in a node, be sure to include at least 2 of them. For example, if we had an XML file (Calendar.xml):

<?xml version="1.0" encoding="utf-8" ?>
<Calendar name="Calendar1">
    <Events>
        <Event id="1" title="Christmas" startDate="2012-12-25" endDate="2012-12-25"/>
        <Event id="1" title="New Years Eve" startDate="2012-12-31" endDate="2013-01-01"/>
    </Events>
</Calendar>

After you have your XML file looking like you want it, you need to generate your XSD (schema definition); with the XML file open, in the Visual Studio menu, go to XML > Create Schema. This should spit out a file that looks something like this:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Calendar">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Events">
          <xs:complexType>
            <xs:sequence>
              <xs:element maxOccurs="unbounded" name="Event">
                <xs:complexType>
                  <xs:attribute name="id" type="xs:unsignedByte" use="required" />
                  <xs:attribute name="title" type="xs:string" use="required" />
                  <xs:attribute name="startDate" type="xs:date" use="required" />
                  <xs:attribute name="endDate" type="xs:date" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
      <xs:attribute name="name" type="xs:string" use="required" />
    </xs:complexType>
  </xs:element>
</xs:schema>

Save this file (File > Save - I didn't really have to tell you that, did I?), in the example, I named it Calendar.xsd.

The next thing you need to do is generate a serializable class to use to interact with your XML file. Open a Visual Studio Command Prompt and navigate to your project folder. Run the XSD (part of the Visual Studio SDK) tool against the XSD file you created:

xsd -c -l:c# -n:Calendar Calendar.xsd

This will generate a Calendar.cs file with serialization info for your XML file.

Back in Visual Studio, you need to include the new files in your project, the easiest way is in the Solution Explorer, make sure that Show All Files is enabled. Find the .xsd file, the .cs file should be nested underneath. Highlight both of these files, right click and Include In Project. At the end of this article, I'll show a way to make this whole process really easy.

You now have a class that is easy to use in code and will allow you very simply read and write to your XML file. To do this, you just use the System.Xml.Serialization.XmlSerializer:

XmlSerializer serializer = new XmlSerializer(typeof(Calendar));
//To load the XML file:
TextReader reader = new StreamReader(Server.MapPath("~/Calendar.xml"));
Calendar calendar;
try
{
    calendar = (Calendar)serializer.Deserialize(reader);
}
finally
{
    reader.Close();
}
//To save the XML file:
TextWriter writer = new StreamWriter(Server.MapPath("~/Calendar.xml"));
try
{
    serializer.Serialize(writer, calendar);
}
finally
{
    reader.Close();
}

Now, for bonus points, you can integrate the XSD => C# generation into Visual Studio with an External Tool. In the Visual Studio menu, go to Tools > External Tools... In the dialog that opens, do the following:

  1. Click Add
  2. Title:  (Whatever makes sense to you)
  3. Command: %programfiles(x86)%\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\xsd.exe (replace %programfiles(x86)% w/ just %programfiles% if you're on a 32-bit version of Windows; and if you are, seriously: why?)
  4. Arguments: -c -l:c# -n:$(ItemFileName) $(ItemFileName)$(ItemExt)
  5. Initial directory: $(ItemDir)
  6. Use Output window = Checked
  7. Click OK

Now all you need to do is select the .xsd file in the Solution Explorer and go to Tools > (Whatever title you gave it).

For a Quadruple Word Score, you can integrate the command into your Solution Explorer context menu:

  1. In the External Tools dialog from before, make a note of what number your tool is in the list (the list is a 1-based index)
  2.  Close the External Tools dialog and right click the toolbar and select Customize...
  3. Select the Commands tab
  4. Select the Context menu radio button and change the dropdown to Project and Solution Context Menus | Item
  5. Click Add Command...
  6. From the Categories list, select Tools
  7. From the Commands list select External Command # where # is the number of your command in the External Commands dialog (from #1, remember? I said make a note of it)
  8. Click OK
  9. (Optional, for a gold star) Click Modify Selection and change the Name to something meaningful.

There you go, Quaduple Word Score + bonus points + a gold star.

If this article helped you, please click the ads at the top to support this blog (unless it's for something obnoxious and/or immoral, then please don't; I don't control which ads are displayed, your browsing history does).

***UPDATE May 31, 2012

I have corrected several typos in one of the code samples:

    calendar = new (Calendar)serialzer.Deserialize(reader);

should read:

    calendar = (Calendar)serializer.Deserialize(reader);