Using the XsltFormatter


Using the XsltFormatter

Although we used the XsltFormatter in previous chapters' examples, we haven't really looked at how it works in detail. You should already be familiar with its basic operation: The XsltFormatter converts notification data into XML and then applies an application-specific XSL transform to obtain a formatted notification message. This section examines the method that the XsltFormatter uses to form the XML representation of the notification data, the structure of the XSL transforms, and support for locale- and device type-specific formatting.

Note

Although we've used the XsltFormatter since the first prototype implementation, you should not think of it as just a prototype component. The XsltFormatter is a powerful tool used to do content formatting in many production notification applications.


Configuring the XsltFormatter in the ADF

When you declare the content formatter for a notification class, you can specify the formatter class name as XsltFormatter. This tells SQL-NS to use the built-in XsltFormatter to format notifications of that notification class. Listing 9.4 shows the <ContentFormatter> declaration in the NewSong notification class in the music store application's ADF.

Listing 9.4. The XsltFormatter Declaration in the NewSong Notification Class Declaration

 <Application>   ...   <NotificationClasses>     <NotificationClass>       <NotificationClassName>NewSong</NotificationClassName>       ...       <ContentFormatter>         <ClassName>XsltFormatter</ClassName>         <Arguments>           <Argument>             <Name>XsltBaseDirectoryPath</Name>             <Value>%_ApplicationBaseDirectoryPath_%\XslTransforms</Value>           </Argument>           <Argument>             <Name>XsltFileName</Name>             <Value>NewSong.xslt</Value>           </Argument>         </Arguments>       </ContentFormatter>       ...     </NotificationClass>   </NotificationClasses>   ... </Application> 

The <ClassName> element specifies XsltFormatter, and the <Arguments> element supplies the two arguments required by the XsltFormatter:

  • XsltBaseDirectoryPath specifies the root directory from which the formatter should load XSL transforms.

  • XsltFileName specifies the name of the XSL transform file to load.

At runtime, the formatter combines the value of these two arguments to locate the XSL transform file.

The XsltFormatter also accepts a third, optional argument: DisableEscaping (not shown in Listing 9.4). Use of this argument is described in the following section.

Input to the XSL Transforms

For each notification that it formats, the XsltFormatter converts the notification data into an XML fragment. This XML fragment is the input to the XSL transform that the formatter applies.

To write XSL transforms for use with the XsltFormatter, you need to understand the format of the input XML. Unfortunately, no single schema describes the input XML. Rather, the XsltFormatter produces an XML structure that depends on the fields in the notification class. In general, the input XML is structured as follows:

 <notifications xml:lang="... Subscriber Locale...">     <notification>         <NotificationField1Name>NotificationField1Value</NotificationField1Name>         <NotificationField2Name>NotificationField2Value</NotificationField2Name>         ...         <NotificationFieldNName>NotificationFieldNValue</NotificationFieldNName>         <ComputedField1Name>ComputedField1Value</ComputedField1Name>         <ComputedField2Name>ComputedField2Value</ComputedField2Name>         ...         <ComputedFieldNName>ComputedFieldNValue</ComputedFieldNName>     </notification>     <notification>         <NotificationField1Name>NotificationField1Value</NotificationField1Name>         <NotificationField2Name>NotificationField2Value</NotificationField2Name>         ...         <NotificationFieldNName>NotificationFieldNValue</NotificationFieldNName>         <ComputedField1Name>ComputedField1Value</ComputedField1Name>         <ComputedField2Name>ComputedField2Value</ComputedField2Name>         ...         <ComputedFieldNName>ComputedFieldNValue</ComputedFieldNName>     </notification>     ... </notifications> 


The outermost element is <notifications>, which has an xml:lang attribute that specifies the locale for which the notification data should be formatted. The <notifications> element can contain one or more <notification> elements, each of which encapsulates the data from a single notification. This allows for digest notifications to be formatted together. When digesting is performed, the <notifications> element contains a <notification> element for each notification in the digest group. If digesting is turned off, the <notifications> element always contains a single <notification> element.

Note

Even with digesting turned on, the <notifications> element contains only a single <notification> element in the case of notifications that are not grouped with other notifications.


Within the <notification> element is a subelement for each field in the notification class schema (including the computed fields). These subelements have the same names as the notification fields, and their values are the notification field values. The following is an example of the XML generated for notifications of the music store application's NewSong notification class:

 <notifications xml:lang="en-US">     <notification>         <ArtistName>Diana Krall</ArtistName>         <Genre>Jazz</Genre>         <SongTitle>I Love Being Here With You</SongTitle>         <AlbumTitle>Live in Paris</AlbumTitle>         <NotificationDateTime>May  7 2004  9:44PM</NotificationDateTime>     </notification>     <notification>         <ArtistName>Diana Krall</ArtistName>         <Genre>Jazz</Genre>         <SongTitle>Let's Fall in Love</SongTitle>         <AlbumTitle>Live in Paris</AlbumTitle>         <NotificationDateTime>May  7 2004  9:44PM</NotificationDateTime>     </notification> </notifications> 


This example shows two notifications digested together. In each <notification> element, there are subelements for the notification fields ArtistName, Genre, SongTitle, and AlbumTitle, and the single computed field, NotificationDateTime.

Note

It's important to pay attention to the casing of the element names in the XML (because XML element names are case sensitive). The <notifications> and <notification> element names are always given in lowercase. The names of the field elements within <notification> have the same casing as the field names defined in the notification class schema in the ADF.


When you understand the pattern that the XsltFormatter uses to construct its XML, you can easily work out what the XML will look like for any notification class. All you need to know is the list of fields in the notification class schema. If you plan to use the XsltFormatter in an application of your own, you will typically use this pattern to create the XML for some sample notifications by hand and use this to develop and test your XSL transforms. The section "Writing an XSL Transform for the XsltFormatter" (p. 326) provides more information on writing your own XSL transforms for use with the XsltFormatter.

Tip

An alternative exists to constructing a sample of the input XML by hand. You can configure the XsltFormatter to use a "pass-through" XSL transform that echoes the input XML without transforming it. Then feed some events and subscriptions into your application and observe the formatter output, as written to the output file by the File delivery protocol. This tells you exactly what XML the XsltFormatter is generating.

You can find the source code for a pass-through XSL transform in the file C:\SQL-NS\Samples\MusicStore\SongAlerts\XslTransforms\PassThrough.xslt. You can specify this transform in the arguments to the XsltFormatter in any application.


If your notification data contains characters that have special meaning in XML, for example, > or <, the XsltFormatter replaces these characters with their equivalent escape sequences (also known as entity references) when forming the XML representation. For example, if the notification data contains the value Five>Four, the XsltFormatter turns this into Five&gt;Four. This ensures that the XML fragment is well formed.

You can use the optional DisableEscaping argument in the XsltFormatter declaration to control its escaping behavior. By default (if the DisableEscaping argument is not specified or set to false), the XsltFormatter replaces all XML reserved characters in the notification data with their equivalent escape sequences. With the DisableEscaping argument set to TRue, the XsltFormatter does not perform escaping, so the notification data passes through unaltered.

If your notification data contains valid XML fragments or escape sequences, set DisableEscaping to TRue so that the formatter places these directly into the XML it passes to the XSL transform. In all other cases, omit the DisableEscaping argument or set it to false.

Writing an XSL Transform for the XsltFormatter

This section looks at an example of an XSL transform that can be used to format notifications with the XsltFormatter. This transform takes the XML that the XsltFormatter generates from the notification data as input and produces a string that represents a formatted notification.

Note

XSL is a sophisticated language that cannot be covered fully in a single section of this book. The example shown here assumes some basic understanding of how XSL transforms work. If you are not familiar with XSL, many good reference books and online tutorials are available for you to consult. The complete XSL specification is available at http://www.w3.org/TR/xslt.


Listing 9.5 shows an XSL transform that produces a formatted notification from NewSong notification data.

Listing 9.5. An XSL Transform That Formats NewSong Notifications

 <?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">     <xsl:output method="text"/>     <xsl:template match="notifications"> New songs are available for download from the music store!         <xsl:apply-templates/> Notification produced at: <xsl:value-of select="notification[1]/NotificationDateTime" />         <xsl:text>         </xsl:text>     </xsl:template>     <xsl:template match="notification">         <xsl:text>         </xsl:text>          Song Title:  <xsl:value-of select="SongTitle" />          Artist Name: <xsl:value-of select="ArtistName" />          Album Title: <xsl:value-of select="AlbumTitle" />          Genre:       <xsl:value-of select="Genre" />     </xsl:template> </xsl:stylesheet> 

The transform is an XML document that conforms to the XSL schema. The root element is <xsl:stylesheet>, and within this element are subelements that describe how the input XML should be transformed.

The <xsl:output> element indicates that the transform outputs plain text. If you write a transform that outputs XML or HTML, you can specify the output type by changing the value of the method attribute on the <xsl:output> element to xml or html.

The <xsl:template> elements define templates that are matched against elements of the input XML. When the XSL processor encounters an element in the input XML, it looks for a matching template in the XSL transform and applies it. In the example shown in Listing 9.5, templates are defined for the <notifications> and <notification> elements of the input XML. Recall that <notifications> is the root element of the input XML. The template that matches <notifications> provides the overall structure for the final message. The template that matches <notification> defines the formatting for each individual notification within the final message.

The <notifications> template outputs a string that appears at the top of the final notification message. Think of this as a heading that appears before the text of the individual notifications. After displaying the heading message, the template specifies <xsl:apply-templates/> to instruct the XSL processor to match any subelements against the appropriate templates. In the final output, <xsl:apply-templates/> is replaced with the text that results from applying the rest of the templates in the XSL transform. After the <xsl:apply-templates/> instruction, the <notifications> template outputs a string that appears at the bottom of the notifications message. This string displays the time that the notification was produced, as given by the value of the NotificationDateTime computed field. Notice that this string selects the value of the NotificationDateTime field from the first <notification> child element.

The template that matches the <notification> element produces text for each individual notification. It displays labels and values for the fields in the notification, using the <xsl:value-of> element to select the values of the field subelements.

When applied to a notification, the transform shown in Listing 9.5 produces output that looks like this:

 New songs are available for download from the music store!         Song Title:  I Love Being Here With You         Artist Name: Diana Krall         Album Title: Live in Paris         Genre:       Jazz         Song Title:  Let's Fall in Love         Artist Name: Diana Krall         Album Title: Live in Paris         Genre:       Jazz Notification produced at: August  15 2005  5:47PM 


Although the example shown here is simple, you can use the full power of XSL to write sophisticated transforms for your own applications. Many XML editors today provide rich support for designing XSL transforms and facilities for testing and debugging them with sample XML inputs. If you're developing a complex XSL transform, these tools can help you work out problems in isolation, before using the transform with SQL-NS. To test your XSL transform, you can either construct a sample XML input by hand using the pattern described in the section "Input to the XSL Transforms," p. 324, or obtain one using a pass-through XSL transform with the XsltFormatter, described in the tip near the end of that section.

Tip

The XsltFormatter caches XSL transforms in memory after reading them from disk the first time. Therefore, if you change the contents of the XSL transform file while the distributor is running, your change will not take effect. To force the XsltFormatter to reload the XSL transform from disk, stop and restart the SQL-NS Windows service.


Using Locale and Device-Specific Transforms

In the examples we've looked at so far, the XsltFormatter uses a single XSL transform to format all notifications. However, the XsltFormatter allows you to provide a different XSL transform for each locale and device type that your application supports. For example, you can write one transform for formatting notifications in U.S. English and another for formatting them in French. Each transform would output words in the right language and provide culture-specific formatting of dates, times, currencies, and other elements.

You can also provide separate transforms for different device types. You can have one transform that formats notifications for cell phones (returning a few lines of plain text as output) and another for email devices (returning HTML-formatted text). The XsltFormatter supports all combinations of locales and device types, so you could actually have separate XSL transforms for U.S. English cell phones, U.S. English email devices, French cell phones, and French email devices. In this section, we look at how to specify different transforms for different locales and device types and then test these transforms with the music store application.

Directory Layout for XSL Transform Files

As you've already seen, in the XsltFormatter declaration in the ADF, you provide an argument called XsltBaseDirectoryPath. This argument specifies the base directory in which the XSL transform files are located. To use specific transforms for different languages and locales, you need to create subdirectories beneath the base directory and place your transform files in them, according to a specific pattern. To understand the directory layout, you first need to understand the way the XsltFormatter locates XSL transforms at runtime.

For each notification, the distributor passes the XsltFormatter the values of the notification fields, as well as the locale and device type for which the notification should be formatted. The XsltFormatter uses the locale and device type values to form directory paths in which it looks for an XSL transform file. The XsltFormatter looks at the following sequence of directories:

  • <XsltBaseDirectoryPath>\<Locale>\<DeviceType>

  • <XsltBaseDirectoryPath>\<Locale>

  • <XsltBaseDirectoryPath>\<DeviceType>

  • <XsltBaseDirectoryPath>

In this list, <XsltBaseDirectoryPath> is the value of the XsltBaseDirectoryPath argument in the XsltFormatter declaration, <Locale> is the particular locale code in the notification data, and <DeviceType> is the device type string. For example, if the XsltBaseDirectoryPath is C:\SQL-NS\Samples\MusicStore\SongAlerts\XslTransforms, for a notification with a locale of en-US and a device type of Email, the XsltFormatter searches the following directories (in order) for the XSL transform file:

 C:\SQL-NS\Samples\MusicStore\SongAlerts\XslTransforms\en-US\Email C:\SQL-NS\Samples\MusicStore\SongAlerts\XslTransforms\en-US\ C:\SQL-NS\Samples\MusicStore\SongAlerts\XslTransforms\Email C:\SQL-NS\Samples\MusicStore\SongAlerts\XslTransforms\ 


In each of these directories, the XsltFormatter looks for an XSL transform file with the name specified in the XsltFileName argument in the content formatter declaration. It loads the transform from the first directory in which it finds it.

You can take advantage of this searching scheme by placing the XSL transforms for specific locales and device types in the appropriate directories. For example, if you wrote a transform to format notifications in French for the Email device type, you would create a directory below the XsltBaseDirectoryPath called fr-FR\Email and place the transform file in it. Similarly, if you wrote a transform for French notifications formatted for the TextMessageDevice device type, you would place it in a subdirectory called fr-FR\TextMessageDevice.

The nature of the searching algorithm used by the XsltFormatter allows you to provide default transforms for use when the formatter cannot find a specific transform for the particular locale and device type of a given notification. This is useful if you don't want to provide specific transforms for each and every locale and device type. For example, if you wanted to provide a single French transform for use regardless of device type, you could create an fr-FR directory without any device type subdirectories beneath it and place your transform there. The formatter's first attempt to load the transform will look for a devices-pecific subdirectory beneath fr-FR and fail to find it. It will then look in fr-FR itself and find the file there. Similarly, you can create a default transform for a particular device type that gets used when the formatter fails to find a locale-specific transform for that device type. By placing a transform in the root of the XsltBaseDirectoryPath, you can provide a default transform for when the formatter cannot locate either a locale- or device-specific transform.

Note

All the transform files in the various directories must have the same name. This name is the value you specify for the XsltFileName argument in the XsltFormatter declaration in the ADF.


Locale and Device-Specific Transforms for the Music Store Application

In this section, we look at several XSL transforms that format the music store's NewSong notifications for various locales and device types. With these transforms placed in files in the directory structure that the XsltFormatter requires, we test the application with some sample events and subscriptions that exercise locale and device-specific formatting.

Take a look at the C:\SQL-NS\Samples\MusicStore\SongAlerts\XslTransforms directory. This is the base directory for XSL transforms in the music store application. You will see a NewSong.xslt transform file in various subdirectories beneath the base directory:

  • en-US\Email\NewSong.xslt Formats notifications for U.S. English on Email devices

  • en-US\TextMessageDevice\NewSong.xslt Formats notifications for U.S. English on TextMessageDevice devices

  • en-US\NewSong.xslt Formats notifications for U.S. English, regardless of device type

  • fr-FR\Email\NewSong.xslt Formats notifications for French on Email devices

  • fr-FR\TextMessageDevice\NewSong.xslt Formats notifications for French on TextMessageDevice devices

  • fr-FR\NewSong.xslt Formats notifications for French, regardless of device type

There is also a NewSong.xslt in the root of the base directory, which will be used if no locale- or device-specific transform can be found.

Note

Earlier in this chapter, in step 4 of the instructions in the section, "Creating the New Instance," (p. 319) you copied the required XSL transform from a supplementary file into the XslTransforms base directory and its en-US sub-directory. This was necessary because previous chapters' examples relied on a different version of the XSL transform in those locations.

The other XSL transform files in the preceding list were already in place and did not require modification for this chapter (they were installed when you originally copied the book's source code in Chapter 2). These files were not used by the examples in previous chapters.


Let's examine the U.S. English transforms first. For email devices, we use the transform shown earlier, in Listing 9.5. This is also used as the default U.S. English transform and the default transform if no locale- or device-specific transform can be found. Thus, a copy of this transform exists in the en-US\Email, en-US, and base directories.

For TextMessageDevices, we need a transform that produces a smaller notification. Listing 9.6 shows this transform.

Listing 9.6. An XSL Transform That Produces Short NewSong Notifications

 <?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">     <xsl:output method="text"/>     <xsl:template match="notifications"> New songs:         Song Title:  <xsl:value-of select="notification[1]/SongTitle" />         Artist Name: <xsl:value-of select="notification[1]/ArtistName" />         Album Title: <xsl:value-of select="notification[1]/AlbumTitle" />         Genre:       <xsl:value-of select="notification[1]/Genre" />       <xsl:text>       </xsl:text>     </xsl:template> </xsl:stylesheet> 

Instead of displaying the data about every notification, this transform produces a message that contains just the data from the first notification. The heading message has also been shortened from that in Listing 9.5, and the message showing the notification time has been removed. This transform can be found in the en-US\TextMessageDevice directory.

For French notifications, we have a French equivalent of both the long U.S. English transform (shown in Listing 9.5) and the short one (shown in Listing 9.6). Listing 9.7 shows the long French transform, used for Email devices and as the default French transform.

Listing 9.7. An XSL Transform That Produces French NewSong Notifications

 <?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">     <xsl:output method="text"/>     <xsl:template match="notifications"> Des nouvelles chansons sont disponibles pour téléchargement au magasin de musique!         <xsl:apply-templates/> Période d'avis: <xsl:value-of select="notification[1]/NotificationDateTime" />         <xsl:text>         </xsl:text>      </xsl:template>      <xsl:template match="notification">         <xsl:text>         </xsl:text>         Titre de chanson: <xsl:value-of select="SongTitle" />         Nom d'artiste:    <xsl:value-of select="ArtistName" />         Album:            <xsl:value-of select="AlbumTitle"/>         Genre:            <xsl:value-of select="Genre" />     </xsl:template> </xsl:stylesheet> 

This transform can be found in the fr-FR\Email and fr-FR directories. It has the same basic structure as the transform in Listing 9.5, but the words have been translated into French.

Listing 9.8 shows the transform that produces shorter French notifications.

Listing 9.8. An XSL Transform That Produces Short, French NewSong Notifications

 <?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">     <xsl:output method="text"/>     <xsl:template match="notifications"> Nouvelles chansons:         Titre de chanson: <xsl:value-of select="notification[1]/SongTitle" />         Nom d'artiste:    <xsl:value-of select="notification[1]/ArtistName" />         Album:            <xsl:value-of select="notification[1]/AlbumTitle" />         Genre:            <xsl:value-of select="notification[1]/Genre" />        <xsl:text>        </xsl:text>     </xsl:template> </xsl:stylesheet> 

This has the same structure as the transform in Listing 9.6. You can find this transform in the fr-FR\TextMessageDevice directory.

Testing the Music Store Application with Locale- and Device-Specific Transforms

The code in the initial ADF you used to create the music store application (earlier in this chapter) configured the XsltFormatter with the appropriate XsltBaseDirectoryPath and XsltFileName arguments for it to work with the transforms described in the previous section. Therefore, no code changes or updates are needed before testing.

To see the locale and device-specific transforms in use, perform the following instructions:

1.

Make sure that you've created the instance as described in the "Creating the New Instance" section (p. 319) earlier in this chapter.

2.

If you haven't already done so, add the subscriber and subscription data as instructed in the "Adding Subscribers and Subscriptions" section (p. 321).

3.

Start the AddSongs program (located in the C:\SQL-NS\Samples\MusicStore\SongAlerts\AddSongs\bin\debug directory). If you had the program open from the previous chapter, before you deleted and re-created the music store database, close and reopen it now. If you have not previously built the AddSongs program, refer to the section "The AddSongs Program" (p. 243) in Chapter 8 for instructions on how to do this.

4.

When AddSongs starts, connect to your SQL Server. If you select SQL Server Authentication, enter the SQL username and password of your development account.

5.

In the main AddSongs dialog box, check the Submit Events for Songs Added box.

6.

Enter the following song data (make sure that you specify the artist name exactly as it appears here; otherwise, the events won't match the subscriptions):

Album Title: Live in Paris
Artist Name: Diana Krall
Genre: Jazz
Song 1: I Love Being Here With You
Song 2: Let's Fall in Love

7.

Click the Add to Database button.

8.

When the form clears, enter the following song data (make sure that you specify the artist name exactly as it appears here; otherwise, the events won't match the subscriptions):

Album Title: Round About Midnight
Artist Name: Miles Davis
Genre: Jazz
Song 1: 'Round Midnight
Song 2: All of You

9.

Wait about 30 seconds and then check the notifications output file, C:\SQL-NS\Samples\MusicStore\FileNotifications.txt.

The notifications output file will contain various notifications formatted for the locales and device types specified in the subscriptions. You can refer back to the subscription data to verify that these appear correctly. Also, notice that the value of the computed field we defined in Listing 9.3, NotificationDateTime, has been included in the notifications formatted by the XSL transforms that reference it.

Note

The text produced by the XSL transforms is in UTF-8 encoding. If you open the notifications output file in a text editor configured to use another encoding, some of the characters in the French notifications may not appear correctly.





Microsoft SQL Server 2005 Notification Services
Microsoft SQL Server 2005 Notification Services
ISBN: 0672327791
EAN: 2147483647
Year: 2006
Pages: 166
Authors: Shyam Pather

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net