Categories
Software Development

C# + XML, the Right Way

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);
Categories
Software Development Technology

Telerik JustDecompile Awesomeness

If you've ever had the (dis?)pleasure of working with me, you know that I am a pretty big fan of the products of Telerik. JustCode and JustDecompile are two of my daily drivers (I didn't realize until JustNow that the title of this blog, JustKarp, JustHappens to match the marketing names of Telerik's productivity tools, I promise that this is JustACoincidence; sorry, I'll stop now). Anyways... I was adding JustDecompile to my External Tools list in Visual Studio today and discovered a second executable in JustDecompile's program directory: JustDecompileCmd.exe (located in %ProgramFiles(x86)%\Telerik\JustDecompile\Libraries). A little digging (OK, I just ran it) revealed that this is a command line version of the program - cool. It turns out that this feature is listed on the JustDecompile Features list on Telerik's website, but it really is downplayed (BTW, this currently only supports C#).

So, I ran the program again, this time pointing it at a .NET assembly (I chose a handy copy of the log4net library) and it spit out 215 files into the folder I specified (C:\log4net), all of which were organized into namespace folders, including a .csproj file that you can, of course, JustOpen (sorry, I can't help myself sometimes) in Visual Studio. The command looked like this:

JustDecompileCmd /target:"C:\log4net.dll" /out:"C:\log4net"

 

Pretty straightforward and Pure. Awesome. Great work Telerik!

P.S.
Here's what I did to add JustDecompile to the Visual Studio External Tools list

Go to: Tools > External Tools... > Add
Title: (Whatever you want)
Command: C:\Program Files (x86)\Telerik\JustDecompile\Libraries\JustDecompile.exe (of course, make this match your environment)
Arguments: $(ItemPath)
Initial directory: $(ItemDir)

Specifying $(ItemPath) & $(ItemDir) will start JustDecompile and attempt to decompile which ever file is highlighted in the Solution Explorer. I did this so I can just hit Show All Files and decompile any assembly in the project's bin.

In the spirit of full disclosure, while I do pay for Telerik subscriptions, as the leader of the Oklahoma City Developer's Group, I have also received complementary licenses of Telerik products as a part of their ongoing support of the .NET development community. This blog post was completely unsolicited by Telerik.