Tag Archives: XML

xQuery and iTunes

It’s been a while since I’ve posted an xQuery. So I dusted off the old saxon processor and thought I’d have a bit of fun with the iTunes library.
I got me some well formed xml by selecting the Podcasts node in iTunes’ left column and exported it to Podcasts.xml (File/Library/Export Playlist). That gives you this xml file. So what now? Run some xQuery against it, is what!

You have to examine the xml a bit to get a feel for how it is laid out. The xQuery which I ran searches for all podcasts by the artist Jupiter Broadcasting and sets out several pieces of meta-data for those podcast episodes (Name, Kind, Bit Rate, Sample Rate). The xQuery is:

declare namespace dave = "http://www.localhost:8090/xquery";

declare function dave:getJupiterBroadcastingShows($allShows as element()+) as element()+
{	
	let $JBShows := $allShows[contains(child::string[preceding-sibling::key[1]/child::text() = 'Artist'], 'Jupiter Broadcasting')]
	return $JBShows
};


let $list := doc("Podcasts.xml")/plist/dict/key[.="Tracks"]/following-sibling::dict[1]
let $key := $list/child::key
let $pods := $key/following-sibling::dict[1]
let $podCastJupiter := dave:getJupiterBroadcastingShows($pods)
return
	<podcasts>
		{
			for $keyVal in $podCastJupiter
			return
				<podcast>
					<name>
						{$keyVal/child::key[child::text() = 'Name']/following-sibling::string[1]/child::text()}
					</name>
					<filetype>
						{$keyVal/child::key[child::text() = 'Kind']/following-sibling::string[1]/child::text()}
					</filetype>
					<bitrate>
						{$keyVal/child::key[child::text() = 'Bit Rate']/following-sibling::integer[1]/child::text()}
					</bitrate>
					<samplerate>
						{$keyVal/child::key[child::text() = 'Sample Rate']/following-sibling::integer[1]/child::text()}
					</samplerate>
				</podcast>
		}
		
	</podcasts>

and the result of this query was:

<?xml version="1.0" encoding="UTF-8"?>
<podcasts>
   <podcast>
      <name>The Computer Action Show! Season 2 Episode 1</name>
      <filetype>MPEG audio file</filetype>
      <bitrate>96</bitrate>
      <samplerate>44100</samplerate>
   </podcast>
   <podcast>
      <name>The Computer Action Show! Season 2 Episode 2</name>
      <filetype>MPEG audio file</filetype>
      <bitrate>96</bitrate>
      <samplerate>44100</samplerate>
   </podcast>
   <podcast>
      <name>The Computer Action Show! Season 2 Episode 3</name>
      <filetype>MPEG audio file</filetype>
      <bitrate>80</bitrate>
      <samplerate>44100</samplerate>
   </podcast>
   <podcast>
      <name>The Computer Action Show! Season 1 Episode 4</name>
      <filetype>MPEG audio file</filetype>
      <bitrate>96</bitrate>
      <samplerate>44100</samplerate>
   </podcast>
   <podcast>
      <name>The Computer Action Show! Season 1 Episode 5</name>
      <filetype>MPEG audio file</filetype>
      <bitrate>96</bitrate>
      <samplerate>44100</samplerate>
   </podcast>
   <podcast>
      <name>The Computer Action Show! Season 1 Episode 6</name>
      <filetype>MPEG audio file</filetype>
      <bitrate>96</bitrate>
      <samplerate>44100</samplerate>
   </podcast>
   <podcast>
      <name>The Computer Action Show! Season 1 Episode 7</name>
      <filetype>MPEG audio file</filetype>
      <bitrate>96</bitrate>
      <samplerate>44100</samplerate>
   </podcast>
   <podcast>
      <name>The Computer Action Show! Season 1 Episode 9</name>
      <filetype>MPEG audio file</filetype>
      <bitrate>96</bitrate>
      <samplerate>44100</samplerate>
   </podcast>
   <podcast>
      <name>The Computer Action Show! Season 1 Episode 10</name>
      <filetype>MPEG audio file</filetype>
      <bitrate>96</bitrate>
      <samplerate>44100</samplerate>
   </podcast>
   <podcast>
      <name>The Computer Action Show! Season 2 Episode 4</name>
      <filetype>MPEG audio file</filetype>
      <bitrate>96</bitrate>
      <samplerate>48000</samplerate>
   </podcast>
</podcasts>

I know I know. It’s awesome.

HTML Email from XML via XSLT

download code for post

During the last week, I had to do a really cool task involving the creation of automated e-mail templates dynamically, using various technologies, including XSLT. I am going to set out a simplified version of that task in this post, as the actual work involved workflows within sharepoint – which is very convoluted.

The first step is to come up with an XML file, from which we can infer an XML schema. The XML file will contain elements that provide the dynamic variables which will be read by the XLST stylesheet during the transform.

<email>
    <firstName>dave</firstName>
    <lastName>rogers</lastName>
    <dateOfBirth>12/12/1900</dateOfBirth>
    <address>
        <number>3</number>
        <street>Smith</street>
        <suburb>Darwin</suburb>
        <state>NT</state> 
        <postcode>0800</postcode>
    </address>
</email>

Now that we have our XML file, we can use the command line tool that comes with visual studio “xsd”, which will create an XML schema based on the structure of the XML file that are created above. (I just kept the xml file and xsd file in the root directory of the C: drive, for simplicity sake).
Xsd.exe
A bit of manual tweaking is required. As you can see, one of the elements is a date type (dateOfBirth). So, we need to change the type of that element in the schema file from an xs:string to an xs:dateTime. Also, through a bit of trial and error, I discovered that I needed to make this element mandatory. To make it mandatory, all that is required is to set both minOccurs and maxOccurs attributes of that relevant element in the schema file to 1. This means that the dateOfBirth element must have at least 1 value, and at most 1 value. I have also made the address element mandatory.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="Email">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="firstName" type="xs:string" minOccurs="0" />
        <xs:element name="lastName" type="xs:string" minOccurs="0" />
        <xs:element name="dateOfBirth" type="xs:dateTime" minOccurs="1" maxOccurs="1" />
        <xs:element name="address" minOccurs="1" maxOccurs="1">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="number" type="xs:string" minOccurs="0" />
              <xs:element name="street" type="xs:string" minOccurs="0" />
              <xs:element name="suburb" type="xs:string" minOccurs="0" />
              <xs:element name="state" type="xs:string" minOccurs="0" />
              <xs:element name="postcode" type="xs:string" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element ref="email" />
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>	

OK. Now is where it starts getting really cool. Download and install Xsd2Code. This is a way cool Visual Studio plugin which creates C# (or VB.NET) classes, that are based on the schema we just created. I’ll walk you through it.

First, create an empty Visual Studio project. Add the schema file email.xsd to the project. As a default behavior, VS creates a DataSet based on the new file. Delete the three files email.Ddesigner.cs, email.xsc and email.xss. They are not required.
Also, go to the Properties window and remove the value MSDataSetGenerator from the Custom Tool property. Again, that would just get in the way.

Now, right-click on the schema file and select “Run Xsd2Code generation”.

Xsd2Code
That will pop up the following properties window:

Xsd2CodeProperties

Have a think about each property. I’d start with the TargetFramework property, as that one can impact the available properties that will be displayed. For my purposes, I was targeting the 2.0 framework, as my client’s SOE was based on .NET 2.0.

When you run the generator, you’ll find a new cs file called email.Designer.cs. For any classes that I found in there, I changed the first letter of the name to upper-case, as per C# convention. I also renamed the file accordingly (Email.Designer.cs).
Now the really, really cool stuff -> lets use it!

I need to create an API which uses this stuff. So, I am adding a class called EmailCreate to the project. This will have the following 2 methods:

  1. CreateEmail ; and
  2. Transform (2 overloads).

I have added a second project in the solution. Just a simple WinForms app. It’s only function, for the purposes of this “tutorial”, will be to display on a form the HTML that will be created using our new EmailCreate API. That’s all we want to see here. The HTML. It will use the API that I have created in the EmailSchema project.
(Note that this is not how one would use the API in the real world. Obviously, you’d use the HTML email in some kind of automated emailing component that takes care of that aspect of things e.g. Sharepoint’s email notification features.)

The key function here is Transform. Basically, this takes 3 inputs and an output. The inputs are:

  1. The email – as created by the CreateEmail method of the EmailCreate class
  2. The xsd schema, which validates the structure of the xml email fragment that gets deserialised from the Email object.
  3. The XSLT stylesheet, which transforms the xml email fragment to the final HTML document.

The output is simply an object which will write the new HTML document to the label on the WinForm.

So where do we get the inputs from?

As mentioned above, the email comes from the CreateEmail method of the EmailCreate class. This is just hard-coded for the purposes of this demonstration.
As can be seen from the following figure, the xsd schema file is embedded in the EmailCreate assembly.

xsd Schema

Embedded Resource in the Assembly

I embedded that file, because it is not meant to change or be tampered with in any way.

I used the following code snippet to retrieve that file as a stream, using reflection:

			//  Get the xsd from the embedded resources of the assembly as a stream.
            Stream xsdStream =
                typeof(EmailSchema.EmailCreate).Assembly.GetManifestResourceStream(
                        typeof(EmailSchema.EmailCreate),
                        "email.xsd"
                        );	

The XSLT stylesheet, on the other hand, is meant to be changeable. Users should be able to choose from more than one stylesheet, depending on their needs, to create the particular email template they require. Templates are able to be added or removed from the XSLT template repository. Here, I have simply retrieved it using an open file dialog. In a real world situation, you would store it in some kind of repository, whether it be a Sharepoint document library or just a directory on a server.

The Transform overload which I chose to use takes Xmlreaders and an XmlWriter as its arguments. The method call looks like this:

            EmailSchema.EmailCreate.Transform(
                XmlReader.Create(emailStream),
                XmlReader.Create(xsdStream),
                XmlReader.Create(xsltFileStream),
                XmlWriter.Create(outputString, new XmlWriterSettings() { Indent = true }));

And voila, one HTML document which you can use as the body of an email message. (It takes a while to perform the transform the 1st time around. So wait a few moments before deciding it has crashed.)

You can download the code for this post here.

Return Some Data As Xml from SQL Server

There are a few cool ways you can pull data out of SQL Server 2005/8 as Xml. That is, it will be returned from your tables and converted to Xml on the fly.

For the purposes of this post, I will be querying the Department table of the AdventureWorks2008 database.

First Query:

SELECT * 
FROM HumanResources.Department
FOR XML RAW

This returns a collection of “row” elements, with each of the fields from your table included as attributes of those elements:

<row DepartmentID="1" Name="Engineering" GroupName="Research and Development" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="2" Name="Tool Design" GroupName="Research and Development" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="3" Name="Sales" GroupName="Sales and Marketing" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="4" Name="Marketing" GroupName="Sales and Marketing" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="5" Name="Purchasing" GroupName="Inventory Management" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="6" Name="Research and Development" GroupName="Research and Development" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="7" Name="Production" GroupName="Manufacturing" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="8" Name="Production Control" GroupName="Manufacturing" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="9" Name="Human Resources" GroupName="Executive General and Administration" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="10" Name="Finance" GroupName="Executive General and Administration" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="11" Name="Information Services" GroupName="Executive General and Administration" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="12" Name="Document Control" GroupName="Quality Assurance" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="13" Name="Quality Assurance" GroupName="Quality Assurance" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="14" Name="Facilities and Maintenance" GroupName="Executive General and Administration" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="15" Name="Shipping and Receiving" GroupName="Inventory Management" ModifiedDate="1998-06-01T00:00:00" />
<row DepartmentID="16" Name="Executive" GroupName="Executive General and Administration" ModifiedDate="1998-06-01T00:00:00" />

Second Query:

SELECT * 
FROM HumanResources.Department
FOR XML AUTO

This returns a collection of “Department” elements. Again, each of the fields from the table is included as attributes of those elements. But the name of the elements are now a little more descriptive than just “row”:

<HumanResources.Department DepartmentID="1" Name="Engineering" GroupName="Research and Development" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="2" Name="Tool Design" GroupName="Research and Development" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="3" Name="Sales" GroupName="Sales and Marketing" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="4" Name="Marketing" GroupName="Sales and Marketing" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="5" Name="Purchasing" GroupName="Inventory Management" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="6" Name="Research and Development" GroupName="Research and Development" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="7" Name="Production" GroupName="Manufacturing" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="8" Name="Production Control" GroupName="Manufacturing" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="9" Name="Human Resources" GroupName="Executive General and Administration" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="10" Name="Finance" GroupName="Executive General and Administration" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="11" Name="Information Services" GroupName="Executive General and Administration" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="12" Name="Document Control" GroupName="Quality Assurance" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="13" Name="Quality Assurance" GroupName="Quality Assurance" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="14" Name="Facilities and Maintenance" GroupName="Executive General and Administration" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="15" Name="Shipping and Receiving" GroupName="Inventory Management" ModifiedDate="1998-06-01T00:00:00" />
<HumanResources.Department DepartmentID="16" Name="Executive" GroupName="Executive General and Administration" ModifiedDate="1998-06-01T00:00:00" />

Third Query:
If we want the enclose the returned results in an element, and represent each field in the result set as an element, this is easily done:

SELECT * 
FROM HumanResources.Department
FOR XML AUTO, ROOT('Departments'),  ELEMENTS;

The return result now has a parent element Departments, and each field is an element:

<Departments>
  <HumanResources.Department>
    <DepartmentID>1</DepartmentID>
    <Name>Engineering</Name>
    <GroupName>Research and Development</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>2</DepartmentID>
    <Name>Tool Design</Name>
    <GroupName>Research and Development</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>3</DepartmentID>
    <Name>Sales</Name>
    <GroupName>Sales and Marketing</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>4</DepartmentID>
    <Name>Marketing</Name>
    <GroupName>Sales and Marketing</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>5</DepartmentID>
    <Name>Purchasing</Name>
    <GroupName>Inventory Management</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>6</DepartmentID>
    <Name>Research and Development</Name>
    <GroupName>Research and Development</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>7</DepartmentID>
    <Name>Production</Name>
    <GroupName>Manufacturing</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>8</DepartmentID>
    <Name>Production Control</Name>
    <GroupName>Manufacturing</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>9</DepartmentID>
    <Name>Human Resources</Name>
    <GroupName>Executive General and Administration</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>10</DepartmentID>
    <Name>Finance</Name>
    <GroupName>Executive General and Administration</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>11</DepartmentID>
    <Name>Information Services</Name>
    <GroupName>Executive General and Administration</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>12</DepartmentID>
    <Name>Document Control</Name>
    <GroupName>Quality Assurance</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>13</DepartmentID>
    <Name>Quality Assurance</Name>
    <GroupName>Quality Assurance</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>14</DepartmentID>
    <Name>Facilities and Maintenance</Name>
    <GroupName>Executive General and Administration</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>15</DepartmentID>
    <Name>Shipping and Receiving</Name>
    <GroupName>Inventory Management</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
  <HumanResources.Department>
    <DepartmentID>16</DepartmentID>
    <Name>Executive</Name>
    <GroupName>Executive General and Administration</GroupName>
    <ModifiedDate>1998-06-01T00:00:00</ModifiedDate>
  </HumanResources.Department>
</Departments>

If you want to keep the fields as attributes, just leave off the “, ELEMENTS;” in the last query. Or, if you just want a collection of elements without a parent, leave off the “ROOT(‘Departments’)” in that query.

So, those commands are pretty cool if you want to generate some xml data to play around with. Or, of course, if you need to in the course of your work!

Representing a SQL Server Tuple with an Xml Field

In my last post, I gave an example of retrieving XML data from an SQL Server relation. So how can we now present that data? As the saying goes, there is more than one way to skin a cat. I’m going to go through two solutions that I came up with off the top of my head.

The primary key of each row correlates to a fragment of XML data. But such a fragment is not very human readable, unless you remove the XML tags. One very easy way of presenting this would be through using an embedded GridView:

note: I did not put any time into presentation. I just wanted to concentrate on functionality here.

To achieve this, I cheated a little bit by binding the XML field to a Label, which I included in the same template column as the embedded GridView (see line 7 for the embedded GridView and line 16 for the Label):

        
            
                
                
                
                    
                        
                                    
                                    
                                    
                                    
                                    
                                    
                            
                        
                        
                    
                                        
            
            
            
            
            
        

Then, in the code behind, I created a DataSet by reading the Text property of the Label into a StringReader. I was able to use that to read in the XML to the DataSet. There are a couple of other small nuances I included which you will see in the following code:

        protected void GridViewEmployees_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType == DataControlRowType.DataRow)
            {
                Label LabelREP = e.Row.FindControl("LabelREP") as Label;
                DataSet dsXml = new DataSet("REP");

                String firstLine = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
                String frag = Server.HtmlDecode(firstLine + LabelREP.Text);
                StringReader readString = new StringReader(frag);

                dsXml.ReadXml(readString);

                GridView GridViewEmployeeDetails = e.Row.FindControl("GridViewEmployeeDetails") as GridView;
                GridViewEmployeeDetails.DataSource = dsXml;
                GridViewEmployeeDetails.DataBind();

                LabelREP.Text = string.Empty;
            }
        }

It is possible to achieve the same thing without using a Label, but I’ll leave that for another day as it is a bit more complicated.

The other approach that I thought of is more interesting than that first approach. With this approach, I am going to use the LINQ object XElement to work with the XML. And then I will dynamically (and manually) build the contents of the “Sales REP” column using a Placeholder, which will replace the embedded GridView that I used in the first example.

So, looking at the template field in the GridView. I now have a placeholder, along with the Label which will initially store the contents of the Xml field:

                <asp:TemplateField HeaderText="Sales Rep">
                    <ItemTemplate>
                        <asp:PlaceHolder ID="PlaceHolderXml" runat="server"></asp:PlaceHolder>
                        <asp:Label ID="LabelREP" runat="server" Text='<%# ((DataRowView)Container.DataItem)["SalesRep"] %>' />
                    </ItemTemplate>
                </asp:TemplateField>

I have included the whole code for the RowDataBound event handler. However, a lot of that code is guarding against situations like when the XML message is empty. The core part to see what I am doing is from line 43. That shows how I parsed the XML fragment into an XElement object and then traversed each of its child nodes to extract the relevant information for display:

        protected void GridViewEmployees_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType == DataControlRowType.DataRow)
            {

                //  Populate PlaceHolder control dynamically with data from the relevent Xml msg.
                PlaceHolder xmlOutput = e.Row.FindControl("PlaceHolderXml") as PlaceHolder;
                Label LabelREP = e.Row.FindControl("LabelREP") as Label;
                string xmlMsg = LabelREP.Text;

                switch (xmlMsg == string.Empty)
                {
                    // if there is no Xml 'Details' field, display that to page;
                    case true:
                        {
                            Literal promptText = new Literal();
                            promptText.Mode = LiteralMode.PassThrough;
                            promptText.Text = "<div class=\"msgContent\" ><div class=\"msgContentHeader\">Sales Rep Details</div> " +
                                "<p> There was no XML field for this blocked item. </p></div><br />";
                            xmlOutput.Controls.Add(promptText);

                            break;
                        }
                    default:
                        {
                            //  Check for empty msg elements
                            switch (xmlMsg.CompareTo("<messages />") == 0)
                            {
                                case true:
                                    {
                                        //  If the msg is empty, write that out to the Page.
                                        Literal promptText = new Literal();
                                        promptText.Mode = LiteralMode.PassThrough;
                                        promptText.Text = "<div class=\"msgContent\" ><div class=\"msgContentHeader\">Sales Rep Details</div> " +
                                            "<p> The xml fragment was an empty element. </p></div><br />";
                                        xmlOutput.Controls.Add(promptText);

                                        break;
                                    }
                                default:
                                    {
                                        //  Get the xml fragment and parse it into an XElement variable 
                                        XElement dbXmlField = XElement.Parse(xmlMsg);

                                        //  Popluate the Placeholder control with content.
                                        Literal wrapperDiv = new Literal();
                                        wrapperDiv.Mode = LiteralMode.PassThrough;
                                        wrapperDiv.Text = "<div class=\"msgContent\" ><div class=\"msgContentHeader\">Sales Rep Details</div>";
                                        xmlOutput.Controls.Add(wrapperDiv);

                                        foreach (XElement el in dbXmlField.Descendants())
                                        {
                                            //  Traverse the descendant nodes, and add them to the 
                                            //  placeholder text where the node itself has only 1 child
                                            //  node i.e. the text node containing the data that we are 
                                            //  interested in.
                                            if (el.DescendantNodes().Count() > 1)
                                                continue;
                                            Literal promptText = new Literal();
                                            promptText.Mode = LiteralMode.PassThrough;
                                            promptText.Text = string.Concat(string.Concat("<div class=\"msgLabel\">", el.Name, ": "), "</div>");
                                            xmlOutput.Controls.Add(promptText);

                                            Literal valueText = new Literal();
                                            valueText.Mode = LiteralMode.PassThrough;
                                            valueText.Text = string.Concat("<div class=\"msgValue\">", el.Value, "</div>");
                                            xmlOutput.Controls.Add(valueText);
                                        }

                                        //  Close out the content being built in the Placeholder.
                                        Literal wrapperDivClose = new Literal();
                                        wrapperDivClose.Mode = LiteralMode.PassThrough;
                                        wrapperDivClose.Text = "</div><br />";
                                        xmlOutput.Controls.Add(wrapperDivClose);
                                        break;
                                    }
                            }
                            break;
                        }
                }
                LabelREP.Visible = false;
            }
        }

And the fruit of my efforts looks something like the following (showing two rows from the result set):

Using LINQ with Dynamic Placeholder