Click here to Skip to main content
15,880,364 members
Articles / Web Development / ASP.NET
Article

Heathmans XmlTransformer Control

Rate me:
Please Sign up or sign in to vote.
4.60/5 (15 votes)
2 May 20048 min read 78.7K   1.2K   35   14
Use XSL to transform your XML content out to your ASPX page with this simple control

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:

XML
<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

  1. 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.
  2. 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.
  3. 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.
  4. 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

XML
<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:

XML
<?xml version="1.0" encoding="utf-8"?>
<content>
    <title>Lorem ipsum doler sit amet</title>    
    <body>
    <![CDATA[
        <p>Lorem ipsum dolor sit amet, 
            consectetuer adipiscing elit.</p>
        <p>Nulla et justo. Mauris non felis. Morbi arcu. 
            Maecenas nulla lorem.</p>        
    ]]>
    </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):

XML
<?xml version="1.0" encoding="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:

  • string ContentFile: This is the path and name to the XML data-source file. e.g. content\samplecontent.xml or just samplecontent.xml. Server.MapPath(".") is used internally to prefix onto this property.
  • string TemplateFile: The path and name to an external XSL template file. e.g. templates\article.xsl or just article.xsl. If you are using inline XSL then this need not be specified. Server.MapPath(".") is used internally to prefix onto this property.
  • string TemplateMatch: Defaulted to /content but you can use this property to override that to whatever your XML requires e.g. rdf:RDF/channel/items/rdf:Seq for a RSS feed. Only used for inline XSL.

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:

C#
protected override void 

Render(HtmlTextWriter writer)
{
    #region Load content, apply XSL and write out to control
    // Load Content XML
    XmlDocument ContentDocument = new XmlDocument();
    ContentDocument.Load(String.Format(@"{0}\{1}", 
      this.Page.Server.MapPath("."), 

this.ContentFile));

    // Load Template XSL
    XmlDocument TransformDocument = new XmlDocument();
    if (this.TemplateFile != null)            
        // Use external template file                
        TransformDocument.Load(string.Format(@"{0}\{1}", 
          this.Page.Server.MapPath("."), this.TemplateFile));            
    else        
    {
        // Use inline template literal content
        string LiteralContent = "";

        // Construct from literal content of this control
        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);
    }
           
    // XSL Transform doc
    XslTransform ContentTransform = new XslTransform();
    // Create an XML Resolver
    XmlResolver ResolverDocument = new XmlUrlResolver();
            
    // Load XSLT
    ContentTransform.Load(TransformDocument, ResolverDocument, 
      this.GetType().Assembly.Evidence);
    // Transform and write out to a string writer
    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

  1. 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.
  2. 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).
  3. 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.
  4. 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

  1. 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.
  2. 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.
  3. 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.
  4. 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer Caliber AI
South Africa South Africa
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.

Comments and Discussions

 
QuestionHow to embed user control in xslt file Pin
phuongduy201010-Dec-08 4:44
phuongduy201010-Dec-08 4:44 
GeneralAn other couple of suggestions Pin
Ernest Bariq22-Jun-04 4:58
Ernest Bariq22-Jun-04 4:58 
GeneralA couple of suggestions Pin
Richard Deeming4-May-04 7:57
mveRichard Deeming4-May-04 7:57 
GeneralRe: A couple of suggestions Pin
Paul Watson4-May-04 8:15
sitebuilderPaul Watson4-May-04 8:15 
GeneralRe: A couple of suggestions Pin
Richard Deeming5-May-04 1:21
mveRichard Deeming5-May-04 1:21 
GeneralRe: A couple of suggestions Pin
Paul Watson5-May-04 2:56
sitebuilderPaul Watson5-May-04 2:56 
GeneralRe: A couple of suggestions Pin
Richard Deeming5-May-04 3:23
mveRichard Deeming5-May-04 3:23 
GeneralEfficiency Pin
Mark Focas3-May-04 21:43
Mark Focas3-May-04 21:43 
GeneralRe: Efficiency Pin
Paul Watson3-May-04 22:19
sitebuilderPaul Watson3-May-04 22:19 
QuestionWhat is the difference.... Pin
leppie3-May-04 11:22
leppie3-May-04 11:22 
AnswerASP.NET XML Webcontrol differences Pin
Paul Watson3-May-04 20:42
sitebuilderPaul Watson3-May-04 20:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.