Tag Archives: XSLT

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.