Calendar

<<  mars 2010  >>
lumamejevesadi
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010

(janvier 21, 2008 08:22)

I have been speaking of XSL in my latest articles, so to conclude (for now) the serie, let's see how we can call a C# (or VB.NET) function inside of an XSL file.

Let's first remember two things

  • you should be able to do whatever you want in XSL. However, as this is a functional language, you may need to forget all what you have learned with your favorites programming languages to suceed to do what you want. For examples, the fact that XSL do not allow to update the content of a variable may render things a bit trickier if you are not used to. Calling a C# function should not necessarily be used as soon as you have a problem
  • Visual Studio.NET allows you to debug an XSL file. This is very practical to validate the transformation you do. However, as soon you will call C# code from the XSL file, this won't be possible anymore. Just think before doing it ! (well... you will be able to debug, but no longer in the XSL editor)

However, despite of these two warnings, you can imagine many scenarii when this will be usefull, starting by code factorisation for example : having a single display or rendering function for C# and XSL is of course of great interest !

So let's imagine you have an XML file like this:

<?xml version="1.0" encoding="utf-8" ?>
<Employees>
   <Employee>
      <FirstName>Pierre-Emmanuel</FirstName>
      <LastName>Dautreppe</LastName>
   </Employee>
</Employees>

And that you already have your formatting function in C# like (of course, this is a very basic example you could very easily do in XSL)

public class MyXslExtension
{
   public string FormatName(string firstName, 
                            string name)
   { 
      return name + ", " + firstName;
   }
}

It's almost finished. Now you just need to use this method in an XSL file. Let's write a very basic one

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:myUtils="pda:MyUtils">
 
<xsl:template match="/Employees">
<Employees>
   <xsl:for-each select="Employee">
   <Employee>
      <xsl:value-of select="myUtils:FormatName(FirstName, 
                                               LastName)" />
   </Employee>
   </xsl:for-each>
</Employees>
</xsl:template>
 
</xsl:stylesheet>

As you can see we have added a new namespace in our file. What is the goal ? To map a function namespace we use in the XSL code to a .NET object we will provide. Note that the function name we use corresponds exactly to the .NET function name we have declared in our object. Note that the value of the namepsace can be whatever you want and will be used later on to do the mapping.

How to use it now ? Let's see the transformation code:

string xslPath = "XSLTFile1.xslt";
string xmlPath = "XMLFile1.xml";
string outputPath = "output.xml";
 
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddExtensionObject("pda:MyUtils", new MyXslExtension());
 
using ( StreamWriter writer = new StreamWriter(outputPath) )
{
   XslCompiledTransform transform = new XslCompiledTransform();
   transform.Load(xslPath);
   transform.Transform(xmlPath, arguments, writer);
}

And here it is ! When launching your code, .NET will automatically map your object to the namespace used in your XSL file.

(janvier 16, 2008 08:21)

We have seen in a previous post how to use an XmlDataSource and a GridView to display a formatted view of an XML document. We have seen also how we could use XSL to help in the formatting.

As you will go on and improve your formatting, you will probably want to add a common XSL file to include in several other XSL using either an <xsl:include or an <xsl:import tag. As soon as you will do that, you will receive an exception on your page "Resolving of external URIs was prohibited." And of course, as your included / imported XSL is in the same directory, you don't really see whay the access to this file is denied.

What's happening ? In fact all included / imported resources are considered as external resources, whatever their storage location. And after investigation in the code of the XmlDataSource, you can see it has not be written to be able to resolve external resources.

It's so impossible to use an XmlDataSource with an XSL using imported or included resources.

So how can we do ? Well... Just forget the XmlDataSource in that case. So how to achieve the same things in code ?

private void BindGridViewToXmlSource()
{
   XmlDocument dom = new XmlDocument();
   dom.LoadXml(GetTransformedXmlData("~/App_Data/YourXslFile.xsl", 
                                     "~/App_Data/YourXmlFile.xml"));
   gvPresentators.DataSource = dom.SelectNodes("YourXPathQuery");
   gvPresentators.DataBind();
}
 
/// <summary>
/// Return the content of the given XML path, 
/// transformed by the given Xsl file
/// </summary>
/// <param name="appRelXslPath">XSL file's app relative path</param>
/// <param name="appRelXmlPath">XML file's app relative path</param>
/// <returns></returns>
private string GetTransformedXmlData(string appRelXslPath, 
                                     string appRelativeXmlPath)
{
   XslCompiledTransform transform = new XslCompiledTransform();
   transform.Load(Server.MapPath(appRelXslPath));
   using ( MemoryStream stream = new MemoryStream() )
   {
      appRelativeXmlPath = Server.MapPath(appRelativeXmlPath);
      transform.Transform(appRelativeXmlPath, null, stream);
      stream.Position = 0;
      using ( StreamReader reader = new StreamReader(stream) )
         return reader.ReadToEnd();
   }
}
(janvier 7, 2008 08:46)

During my last days of vacations, I have decided to add a new page in my blog, listing the different trainings I gave these last years. My idea ? To have a simple page, based on a XML file, easy to maintain and that would be able to display several views on fhe same information. So I have immediately been thinking to using a GridView and an XmlDataSource. Here is below some tips about the combination of the two.

1. Using a GridView to display attributes from an Xml file

By default, XmlDataSources and GridView will work to display tabular data from an Xml file, and so will based all its behavior on XML attributes.

Let's imagine you have a simple XML file like this:

<?xml version="1.0" encoding="utf-8" ?>
<Trainings>
   <Training presentator="Pierre-Emmanuel Dautreppe"
             title="My Training title"
             description="This is my description" 
             date="2007-12-01" />
</Trainings>

you could very simply create a GridView to display the data :

<asp:GridView ID="GridView1" runat="server"
   DataSourceID="XmlDataSource1"
   AutoGenerateColumns="false" >
   <Columns>
      <asp:BoundField HeaderText="Title" DataField="Title" />
      <asp:BoundField HeaderText="By" DataField="Presentator" />
   </Columns>
 </asp:GridView>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" 
   DataFile="~/App_Data/Trainings/AttributesTrainings.xml" 
   XPath="Trainings/Training" />
Be careful to check that your DataFile correspond to your Xml File
And  you are done ! Here is your GridView :

2. Using a GridView to display elements from an Xml file

Of course, in some case, this won't work because you may not have all your data in attributes. Let's imagine for example you have to add some special characters, or using a collection, or ... There would be plenty of good reasons to have an XML structure more complex. So let's imagine your XML file is now like :

<?xml version="1.0" encoding="utf-8" ?>
<Trainings>
   <Training title="My Training title"
             description="This is my description"
             date="2007-12-01">
      <Presentator>
         <![CDATA[Pierre-Emmanuel Dautreppe & Other]]>
      </Presentator>
   </Training>
</Trainings>

You can simply update slightly your GridView to have

<asp:GridView ID="GridView1" runat="server" 
   DataSourceID="XmlDataSource1" AutoGenerateColumns="false" >
   <Columns>
      <asp:TemplateField HeaderText="By">
         <ItemTemplate><%# XPath("Presentator")%></ItemTemplate>
      </asp:TemplateField>
      <asp:BoundField HeaderText="Title" DataField="Title" />
   </Columns>
 </asp:GridView>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" 
   DataFile="~/App_Data/Trainings/ElementsTrainings.xml" 
   XPath="Trainings/Training" />
Be careful to check that your DataFile correspond to your Xml File

3. Using XSL files to tranform an XML file and facilitate the data display

In the end you will probably have Xml files much more complex. But this is not a problem as you can always update your XmlDataSource definition to reference an Xsl file. With it, you will be able to do whatever you want, and so in fine to arrive to an Xml structure like one of the two previous ones.

Let's take an example with this xml file :

<?xml version="1.0" encoding="utf-8" ?>
<Trainings>
   <Presentators>
      <Presentator id="1">
         <FirstName>Pierre-Emmanuel</FirstName>
         <Name>Dautreppe</Name>
      </Presentator>
      <Presentator id="2">
         <FirstName>FirstName</FirstName>
         <Name>OtherName</Name>
      </Presentator>
   </Presentators>
   <Trainings>
      <Training>
         <Presentators>
            <Presentator>1</Presentator>
            <Presentator>2</Presentator>
         </Presentators>
         <Title><![CDATA[Titre & 1]]></Title>
         <Description></Description>
         <Date></Date>
      </Training>
   </Trainings>
</Trainings>

We can create an XSL file to convert it (note that I'm not an XSL expert... if you see any error or possible improvement, leave a comment !):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
<xsl:template match="/">
   <Trainings>
      <xsl:apply-templates select="/Trainings/Trainings" />
   </Trainings>
</xsl:template>
 
<xsl:template match="/Trainings/Trainings">
   <xsl:for-each select="Training">
<Training>
   <Presentator>
      <xsl:for-each select="Presentators/Presentator">
         <xsl:variable name="id" select="." />
         <xsl:value-of 
            select="//Presentators/Presentator[@id=$id]/Name"/>
         <xsl:if test="position()!=last()">, </xsl:if>
      </xsl:for-each>
   </Presentator>
   <Title><xsl:value-of select="Title"/></Title>
   <Description><xsl:value-of select="Description"/></Description>
   <Date><xsl:value-of select="Date"/></Date>
</Training>
   </xsl:for-each>
</xsl:template>
 
</xsl:stylesheet>

With this new XML file and the corresppoonding XSL, we can now update the aspx page as follows

<asp:GridView ID="GridView2" runat="server" 
   DataSourceID="XmlDataSource2" AutoGenerateColumns="false" >
   <Columns>
      <asp:TemplateField HeaderText="By">
         <ItemTemplate><%# XPath("Presentator")%></ItemTemplate>
      </asp:TemplateField>
      <asp:TemplateField HeaderText="Title">
         <ItemTemplate><%# XPath("Title")%></ItemTemplate>
      </asp:TemplateField>
   </Columns>
 </asp:GridView>
<asp:XmlDataSource ID="XmlDataSource2" runat="server" 
   DataFile="~/App_Data/Trainings/Trainings.xml" 
   TransformFile="~/App_Data/Trainings/TrainingDisplay.xsl"
   XPath="Trainings/Training" />

And here it is ! Once again, the display works perfectly:

Powered by BlogEngine.NET 1.2.0.0 | Theme by Pierre-Emmanuel Dautreppe