Introduction
The Microsoft .NET framework makes it very easy to serialize an object to XML. It has its shortcomings which I won't go into here, but all-in-all, it is quite useful. Something I've wanted to do for some time now is to write out (serialize) an object in XML format while including a stylesheet reference..
Description of the Problem
For this article, I am using a simple class called Dictionary
. It is just a list of words, where the Word
class is just a combination of a value (the word) and its definition. Here is the Dictionary
class:
using System;
using System.Collections.Generic;
using System.Text;
namespace WriteXmlWithStyle
{
[Serializable]
public class Dictionary
{
List<Word> m_Words =
new List<Word>();
public List<Word> Words
{
get { return m_Words; }
set { m_Words = value; }
}
}
}
And here is the Word
class:
using System;
using System.Collections.Generic;
using System.Text;
namespace WriteXmlWithStyle
{
[Serializable]
public class Word
{
private string m_strWord;
private string m_strDefinition;
public Word()
{
}
public Word(string word, string def)
{
m_strWord = word;
m_strDefinition = def;
}
public string Value
{
get { return m_strWord; }
set { m_strWord = value; }
}
public string Definition
{
get { return m_strDefinition; }
set { m_strDefinition = value; }
}
}
}
It doesn't really matter what objects we are serializing, but it helps to get a look at these before we start looking at them in their serialized form.
Objects are normally serialized to XML like this:
private void SerializeWithoutStylesheet(Dictionary
dictionary, Stream stream)
{
StreamWriter writer = new StreamWriter(stream);
try
{
XmlSerializer serializer =
new XmlSerializer(typeof(Dictionary));
serializer.Serialize(writer, dictionary);
}
finally
{
writer.Flush();
}
}
which produces an easy to read XML document like this:
="1.0"="utf-8"
<Dictionary
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Words>
<Word>
<Value>House</Value>
<Definition>A place where people
live providing shelter</Definition>
</Word>
<Word>
<Value>Car</Value>
<Definition>A device to transport people</Definition>
</Word>
<Word>
<Value>Wood</Value>
<Definition>Part of a tree</Definition>
</Word>
<Word>
<Value>Serializer</Value>
<Definition>An object for transforming another object to or
from a linear sequence of bytes</Definition>
</Word>
</Words>
</Dictionary>
OK. Now, we get to the cool stuff. XML stylesheets give us the ability to transform this XML into another form. Normally, when you open up an XML file in a web browser, you see the raw XML (albeit formatted):

However, we can transform the XML into HTML (using a stylesheet), to produce this instead:

The Solution
In order to write out a stylesheet reference, we need to do two things:
- Create the stylesheet that does the transformation, and
- Add a processing instruction to the XML to reference the stylesheet.
Here is the stylesheet source to produce the above table:
="1.0"="utf-8"
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head>
<title>My Dictionary</title>
<link href="Dictionary.css"
rel="stylesheet"
type="text/css" />
</head>
<body>
<div align="center">
<h1>My Dictionary</h1>
</div>
<table border="1"
cellpadding="3"
cellspacing="0">
<tr>
<th>Word</th>
<th>Definition</th>
</tr>
<xsl:for-each select="//Words/Word">
<tr>
<td>
<xsl:value-of select="Value"/>
</td>
<td>
<xsl:value-of select="Definition"/>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The only other thing we need to do now is add the stylesheet reference. To do this, we add one line near the top of the file.
='text/xsl'='Dictionary.xslt'
Here is what the XML document would look like with this reference:
="1.0"
='text/xsl'='Dictionary.xslt'
<Dictionary
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Words>
<Word>
<Value>House</Value>
<Definition>A place where people live
which provides shelter</Definition>
</Word>
<Word>
<Value>Car</Value>
<Definition>A device to transport people</Definition>
</Word>
<Word>
<Value>Wood</Value>
<Definition>Part of a tree</Definition>
</Word>
<Word>
<Value>Serializer</Value>
<Definition>An object for transforming another object
to or from a linear sequence of bytes</Definition>
</Word>
</Words>
</Dictionary>
The problem is that when you use .NET's XmlSerializer.Serialize
method to write out the object in XML format (see above), it doesn't normally write any stylesheet references (AKA processing directives). The trick is to first get the XML data into an XmlDocument
and then to attach the processing instructions. Here is the code to do this:
private void SerializeWithStylesheet(Dictionary
dictionary, Stream stream, string stylesheet)
{
MemoryStream memXmlStream = new MemoryStream();
XmlSerializer serializer =
new XmlSerializer(typeof(Dictionary));
serializer.Serialize( memXmlStream, dictionary );
XmlDocument xmlDoc = new XmlDocument();
memXmlStream.Seek(0, SeekOrigin.Begin);
xmlDoc.Load(memXmlStream);
XmlProcessingInstruction newPI;
String PItext = string.Format("type='text/xsl' href='{0}'", stylesheet);
newPI = xmlDoc.CreateProcessingInstruction("xml-stylesheet", PItext);
xmlDoc.InsertAfter(newPI, xmlDoc.FirstChild);
XmlTextWriter xmlWriter = new XmlTextWriter(stream,
System.Text.Encoding.ASCII);
xmlDoc.WriteTo(xmlWriter);
xmlWriter.Flush();
}
Unfortunately, this involves serializing the object to a stream, deserializing it into a XmlDocument
, and then reserializing it after first attaching the processing directive. It isn't the most efficient way to do this, but it's the simplest approach that I've found so far. An alternative would be to write your own serializer. Writing your own isn't that difficult, but it is definitely harder than this, and must also be revised every time you revise your objects.
Using the code
To do this for one of your own objects, you just have to do the following:
- Take the necessary steps to serialize your class using the
XmlSerializer
class. Mostly, this just involves adding the [Serializable]
attribute as you see above.
- Create an XML stylesheet.
- Take the above
SerializeWithStylesheet
function and modify it to suit your own needs.
Points of Interest
In spite of their complexity, I am a big fan of stylesheets. They allow me to take out the functionality that was previously embedded into my application, and instead put it into a configuration file that the user can modify.
Links of Interest
History