Summary
XmlTransformer is an ASP.NET web-control which makes extracting XML content into your ASPX page fast and easy. You can use use simple or complex XSL to extract the XML bound data. Your XSL template can be inline with your ASPX page or in a seperate XSL template file.
Here is a quick usage example, see below for more:
<Heath:XmlTransformer ContentFile="samplecontent.xml" Runat="server">
<h2><xsl:value-of select="title" /></h2>
<p><strong><xsl:value-of select="body" /></strong></p>
</Heath:XmlTransformer>
Requirements
You need to know ASP.NET and C# (the control is written in C#). XSL and XML knowledge is recommended too or you may find understanding the point of the control hard. In general web-development skills are required.
The Microsoft .NET Framework v1.1 was used in creating this control and is required.
Introduction, The Problem
The number of times I have written content-extracting code for website projects would drive a saner man to choose a different career. Over the years I have written numerous controls project-by-project to speed up displaying content from a data source on a web page. The problem has been re-using these controls between different projects. Invariably the control would provide for a title, body and footer and the next project would need an inline-image or a sub-header too. Most of the time it was not worth re-using a modified similar control and I would end up writing a new one just for that project.
They were never flexible enough for my needs. I needed a control that would connect to a data-source and then replace field-placeholders with the content-instance's field value.
That is when I realised XSL was exactly what I needed. The only problem was that usually you transform an entire XML document against an XSL template file and end up with, in my case, a whole XHTML document to be sent back to the client. I did not want that as I still wanted ASP.NET to do it's magic with other controls, handle viewstate and everything else it does so well.
I needed to wrap up the XML-XSL transform code and have it spit out a fragment of XHTML which could be inserted into any part of an ASPX page.
My Requirements
- Ease-of-use. I simply won't use a control that requires major setup or pages of code. In those cases I normally roll my own custom-suited control rather than use a third party control that may need some retrofitting. And if I am not about to use a control I can guarantee the rest of my team won't.
- Flexibility. This content control needed to be able to support anything from a one-field content system to multiple fields with repeater/list capabilities. So if one page needs title, body and footer fields while another needs just title and body then the control should easily cater for this.
- Common content store. Preferrably I wanted SQL and XML support but just XML is fine as content is ideally suited to the XML format. Also seperate XML files have proven to be more flexible in the long run when managing content on websites.
- Little or no extra skills. The rest of my team does not know XSL. Trying to get them to creat XSL template files would result in them simply not using the control. So any XSL the control used needed to be the absolute basics and not require seperate files in any way (except the data-source of course). This requirement though had to be balanced against #2 to ensure advanced-usage was not crippled.
Really, the main requirement was for a developer to be able to open an ASPX file, bang in a few lines of markup and have content stream in from an XML document. XmlTransformer does just that with maximum flexibility.
My Solution
<Heath:XmlTransformer ContentFile="samplecontent.xml" Runat="server">
<h2><xsl:value-of select="title" /></h2>
<p><strong><xsl:value-of select="body" /></strong></p>
</Heath:XmlTransformer>
XmlTransformer
wraps up the XmlDocument
and XslTransform
classes of System.XML
behind a few simple properties and minimal XSL. It loads the separate XML data-source document, reads in the inline XSL and spits out the transformed content.
In the above example we have the ContentFile
attribute/property specifying the XML data-source to load. Inline we have the normal XHTML elements of h2
and p
wrapped around the simplest of XSL functions, xsl:value-of
.
The XML data-source for that would be:
="1.0"="utf-8"
<content>
<title>Lorem ipsum doler sit amet</title>
<body>
<![CDATA[
</body>
</content>
So simply a root element of content
with two child nodes of title
and body
.
XmlTransformer
by default sets the XSL root template match to /content
. This can be overriden by specifying the TemplateMatch
attribute for when your XML data-sources have a root element other than content
.
XmlTransformer
will generate the following XSL template in memory when processing (this is never saved to an external file):
="1.0"="utf-8"
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/content">
<processedContent>
<h2><xsl:value-of select="title" /></h2>
<p><strong><xsl:value-of select="body" /></strong></p>
</processedContent>
</xsl:template>
</xsl:stylesheet>
processedContent
is automatically wrapped around inline XSL by XmlTransformer
to ensure the transformed XML does not have multiple root elements. Your XSL template file does not need it, but it will still generate an error if your XSL transforms to XML with multiple root elements.
You can put almost any XSL syntax inline. You may want to use the xsl:for-each
function to list multiple elements from the XML data-source. The source code ZIP above includes an example using this. You can even use the xsl:attribute function to insert content into attributes of XHTML elements e.g. <a><xsl:attribute name="href"><xsl:value-of select="link" /></xsl:attribute>Lorem ipsum</a>
As stated I required flexibility with ease of use and I think I have achieved this reasonably well. The rest of my team need to know minimal XSL to handle most content situations while I can build XSL templates for complex situations and you really can just bang the control in and go.
Properties and Methods
There are no public methods actually. The control runs itself by overriding the Render
event. Public properties are almost as simple:
And that is that, really simple. The flexibility comes from the XSL capabilities.
Internally there is just one private property, string
XSLTemplate
, which holds the XSL template code to wrap around the inline XSL.
Noteworthy Code
There is not very much. As mentioned XmlTransformer
is a very basic wrapper for the XmlDocument
and XslTransform
classes. It simply means that I don't have to instantiate those two classes and use them everytime I want to transform some XML.
Here is the main part of the code:
protected override void
Render(HtmlTextWriter writer)
{
#region Load content, apply XSL and write out to control
XmlDocument ContentDocument = new XmlDocument();
ContentDocument.Load(String.Format(@"{0}\{1}",
this.Page.Server.MapPath("."),
this.ContentFile));
XmlDocument TransformDocument = new XmlDocument();
if (this.TemplateFile != null)
TransformDocument.Load(string.Format(@"{0}\{1}",
this.Page.Server.MapPath("."), this.TemplateFile));
else
{
string LiteralContent = "";
foreach(Control CurrentControl in this.Controls)
if (CurrentControl is LiteralControl)
LiteralContent += ((LiteralControl)CurrentControl).Text;
else
throw new Exception(
"Only controls of type Literal allowed");
TransformDocument.InnerXml = string.Format(
xSLTemplate, LiteralContent, this.TemplateMatch);
}
XslTransform ContentTransform = new XslTransform();
XmlResolver ResolverDocument = new XmlUrlResolver();
ContentTransform.Load(TransformDocument, ResolverDocument,
this.GetType().Assembly.Evidence);
StringWriter TransformedDocument = new StringWriter();
ContentTransform.Transform(ContentDocument, null,
TransformedDocument, ResolverDocument);
ContentDocument.InnerXml = TransformedDocument.ToString();
writer.Write(ContentDocument.DocumentElement.InnerXml);
#endregion
}
A rundown of the code would go; We load up the external XML data-source, then decide to either load an external XSL template file or use inline XSL. Next we create the XslTransform
document, create an XmlResolver
, load the XSL into the transform document, run the transform out to a StringWriter
and put that into an XmlDocument
. Then all the generated XML from the root element down is written out to the ASPX page.
Conclusion
For me this is where ASP.NET over ASP (or PHP) really shines. The possibility of wrapping up complex utilities in a simple re-usable control. I don't have to know how to handle XSL or XML and credentials, the BCL does that all for me. All I do is harness it all together into a powerful and easy to use black box.
Now I have a very easy to use control which I can reference into any project and display content in a flexible manner.
Notes
- Heathmans is the code-name for our internal common-code library. Heath is the shortened version. Feel free to change the namespaces as you see fit when using it.
- The class name of
XmlTransformer
came from David Wulff after I had pestered him for ages about a suitable name. I repayed him by playing 30 minutes of awful backgammon over MSN Messenger (he won). Yahtzee is far more fun (I won).
- In .NET Framework v1.0 you could just do an XSL transform with
ContentTransform.Transform(inputfile, outputfile)
but this has been depreciated in v1.1. In the latter version you need an XML resolver and credentials to ensure security. Check out line 96 onwards of the source code for more on this. It requires a separate article to explain properly, all I know is that the code I have used works.
- ASP.NET contains a web-control called XML. It is similar to mine but the main difference is that it does not allow inline XSL while mine does. In the future I may inherit from the XML web-control and extend it. Thanks to Leppie for the heads-up.
Known Issues & Limitations
- You cannot have anything but literal "controls" inside this control. I could have just let them render out but then they would not be in order in relation to the literal controls as the literal control contents are concatenated to form the inline XSL template. If anybody has a bright idea for getting around this issue please speak up, thanks.
- It only supports an XML data source at the moment. With some SQLXML trickery or
DataSet
usage though I am sure I can make this control use a SQL data provider. All our projects either use SQL or XML for their data stores and any other formats or providers won't be catered for unless you code it yourself.
- When using inline XSL you are limited to one template match in
XmlTransformer
. If you need more than one XSL template match block then use an external XSL file.
- If you spot any code that could do with some changing, tell me. Got a better way? Tell me. Any help is appreciated, I don't have a Code Ego at all. I just want code to work so I can get to the bar earlier.
My name is Paul Watson and I have been a professional web-developer since 1997. I live and work remotely from Cape Town, South Africa.
I have many years of experience with HTML, CSS, JavaScript, PostgreSQL, and Ruby on Rails. I am capable in Python and Machine Learning too.
Currently I am the CTO of CaliberAI. Formerly I worked with Kinzen (CTO & co-founder), Storyful (CTO, acquired by News Corp), FeedHenry (co-founder, acquired by Red Hat), and ChangeX.
Now that you know a bit about me why not say hello.