Web caching, Part 3: Database Integration

The last article in my Web caching series published by InformIT.com details the database integration methods. As most SQL databases don't give you record modification timestamps, you have to implement them yourself. In this article, you'll see how you can use SQL triggers to implement table-level, record-level and join-level timestamps that can be used to set the last-modified date on an HTTP response.

XSL: create optional class attributes in HTML output

When generating styled HTML output from XML, you might need to attach a special class to the first and/or the last element in a series (for example, first and last child). An only child would obviously have both classes attached to it (don't forget, class attribute can list multiple classes separated by a whitespace). The code to generate optimal list of HTML classes (or doing a generic join of list elements) is pretty simple:
<xsl:variable name="class">
<xsl:if test="position() = 1"> FirstInSeries</xsl:if>
<xsl:if test="position() = last()"> LastInSeries</xsl:if>
</xsl:variable>

<xsl:if test="$class != ''">
<xsl:attribute name="class">
<xsl:value-of select="substring-after($class,' ')" />
</xsl:attribute>
</xsl:if>
Please note the following:
  • Each value in the list has a leading whitespace. The list values are thus properly separated, but the result has an extra leading whitespace.
  • The last xsl:if instruction tests if the class attribute is needed (detailed description of this trick).
  • The substring-after function removes the leading whitespace, resulting in the desired value for the class attribute.

XSL: avoid empty attributes in output stream

Whenever you create an attribute with an xsl:attribute instruction containing conditional instructions (xsl:if or xsl:choose), the result might be empty, resulting in an empty attribute in the output stream. For example, both my simplistic WordProcessingML to HTML stylesheet or similar code published on OpenXML Developer could create <span style=""> elements.

To avoid this problem, capture the conditional results into a variable and create the attribute only if the variable is non-empty, for example:
<xsl:variable name="style">
<xsl:if test="w:rPr/w:i">font-style: italic;;</xsl:if>
<xsl:if test="w:rPr/w:b">font-weight: bold;</xsl:if>
</xsl:variable>

<xsl:if test="$style != ''">
<xsl:attribute name="style"><xsl:value-of select="$style" /></xsl:attribute>
</xsl:if>

XSL: transform an element only if it's a descendant of another element

I recently had to work on WordProcessingML documents and wanted to transform only those w:p elements that were within the subtree of the w:body element. My initial solution was "a bit" complex: match all w:p elements that have a w:body ancestor.
<xsl:template match="w:p[ancestor::w:body]">
There is (as always) a much more elegant solution:
<xsl:template match="w:body//w:p">

XSL: Detect first-of-type element in a list

Sometimes you want your XSL transformation to process first element of a type in a child list in different manner. For example, using the following data ...
<?xml version="1.0" encoding="UTF-8" ?>
<list>
<author>John Brown</author>
<author>Jim Small</author>
<editor>Jane Doe</editor>
<editor>Grace Kelly</editor>
</list>
... you might want to process the first editor in a slightly different manner. There are two simple solutions:

(A) Write two transformation rules, one for the first element, one for the remaining ones:
<xsl:template match="editor[1]">
<!-- transform the first editor in list -->
</xsl:template>

<xsl:template match="editor">
<!-- transform the remaining editor elements -->
</xsl:template>
(B) Use preceding-sibling axis to check whether an element is the first of its type:
<xsl:template match="editor">
<xsl:if test="not(preceding-sibling::editor)">Editors:</xsl:if>
<xsl:value-of select="text()" /> (<xsl:value-of select="position()" />)
</xsl:template>
You should use the first method when the handling of the first element is radically different from the rest and the second one when you only need to set a few attributes or write a lead-in text.

IE7 breaks some AJAX libraries

Probably this is very old news (and Internet is full of related blog entries), but somehow I've managed to miss it ... If you use XMLHTTP Request object and initialize it similar to this:
if (IE) {
x = new ActiveXObject("Microsoft.XMLHTTP")
} else {
x = new XMLHttpRequest();
}
... Internet Explorer 7 will constantly complain about the page trying to use ActiveX controls (at least IE6 was silent unless you've disabled ActiveX in which case AJAX broke anyway). The proper way to deal with this particular quirk of IE is to test for window.XMLHttpRequest first (instead of relying on browser type) and use ActiveX only if needed.

Note: If you use Sarissa (which broke my AJAX application), download the latest version, it contains all relevant IE7 fixes.

XPath position() function returns unexpected results

If you process XML documents generated "manually" (including documents composed within a program without proper XML tools), you might be surprised by the results returned by with position() function. For example, the input XML document ...
<?xml version="1.0" encoding="UTF-8" ?>
<list>
<author>John Brown</author>
<editor>Jane Doe</editor>
<author>Jim Small</author>
<editor>Grace Kelly</editor>
</list>
... processed with a simple XML stylesheet ...
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:output method="text" />

<xsl:template match="list">
Results: <xsl:apply-templates />
</xsl:template>

<xsl:template match="author|editor">
<xsl:value-of select="text()" /> (<xsl:value-of select="position()" />)
</xsl:template>

</xsl:stylesheet>
... returns surprising results:
  Results:
John Brown (2)

Jane Doe (4)

Jim Small (6)

Grace Kelly (8)
The reason for this unexpected behavior are whitespace text nodes between the author and editor elements which are also counted by the position() function. To skip them, either create XML documents without extra whitespace or use more specific xsl:apply-template statements, for example:
<xsl:template match="list">
Results: <xsl:apply-templates select="*"/>
</xsl:template>
The select="*" option in the last example selects only child nodes of the current XML node and thus skips over text fragments (including whitespace nodes).

Web Caching, Part 2: Reduce the Download Time

In my second article in the Web caching series published by InformIT.com, I've focused on caching dynamically generated pages in the browser cache. The article explains the in-depth details of browser-side HTTP caching and the HTTP headers you have to process in server scripts to make your dynamic pages cacheable.

When properly implemented, this solution can drastically reduce the amount of information downloaded to visitor's browser, thus increasing the overall responsiveness of your web application.

Web Caching, Part 1: Explicit Content Expiration

InformIT.com has published Reap the Benefits of Web Caching, Part 1: Explicit Content Expiration; the first article in my three-part series covering usage of web caching mechanisms in dynamic web pages.

Flash and Search Engines

This article gives you an excellent idea how to use Flash in your web sites while still having them completely visible to the search engines.

Detect included libraries in ASP

My ASP applications rely a lot on included libraries. As I usually end up with a complex hierarchy of libraries, it's not always possible to include the required modules in the dependent library. This makes debugging a bit more interesting, as you cannot be sure all the prerequisites have been included in the main .asp page unless you test all code paths.

To work around this problem, I wrote a small routine that checks for existence of prerequisite functions and throws an exception documenting what went wrong.
'
' CheckIncludedLibrary - checks if the incRoutine is available,
' otherwise throws an exception documenting that srcLibrary
' needs incLibrary
'
Sub CheckIncludedLibrary(incRoutine,srcLibrary,incLibrary)
Dim routineRef

On Error Resume Next
Set routineRef = GetRef(incRoutine)
If Err.Number <> 0 Then On Error Goto 0 : _
Err.Raise vbObjectError+1,srcLibrary, _
"You have to include " & incLibrary
End Sub

You would use this routine in a way similar to the example below:
'
' This library requires inclusion of /forms/xmlLibrary.asp,
' /forms/lib/editLibrary.asp and /asp/incPostingPreview.asp
'

CheckIncludedLibrary "NewXMLTextElement", _
"listRoutesLibrary","/forms/xmlLibrary"
CheckIncludedLibrary "EnrichElementText", _
"listRoutesLibrary","/forms/lib/editLibrary"
CheckIncludedLibrary "PostToPreview", _
"listRoutesLibrary","/asp/incPostingPreview"

“You've asked for it” series

Analyzing Google query strings that brought visitors to my blog (StatCounter is an excellent free tool to do this job), I usually find interesting (oft repeating) queries that are not yet answered in my blog. Obviously there are not too many good answers on other web sites, otherwise Google users would probably not click on a hit on the second or third page (where my blog usually appears for more generic queries).

So, to help my fellow programmers, I've started a series of “You've asked for it” posts answering the questions that brought many of you to my site in the first place (and, don't forget, you can always send me an interesting question with the Send a message link on my bio page.

Use XSLT to generate RSS item description

RSS specifications do not allow HTML markup in title or description elements. If you want to include HTML markup in RSS elements, it has to be quoted, for example <b>bold</b>. Doing this in any server-side scripting language is easy, for example, ASP provides Server.HTMLEncode function. If you use XSLT to transform internal XML documents into RSS feeds, the task gets trickier, as XSLT provides no equivalent function.

The following XSLT templates solve the problem: call the outputQuotedTree template in the context of input node containing HTML markup and it will generate quoted contents of its child nodes.

<xsl:template name="outputQuotedTree">
<xsl:for-each select="node() ¦ text()">
<xsl:call-template name="outputTextNode" />
</xsl:for-each>
</xsl:template>


<xsl:template name="outputTextNode">
<xsl:choose>
<xsl:when test="name() = ''">
<xsl:value-of select="." />
</xsl:when>
<xsl:otherwise>
<!-- Emit the opening tag -->
<xsl:text>&lt;</xsl:text><xsl:value-of select="name()" />
<!-- Emit the attributes of the opening tag -->
<xsl:for-each select="@*">
<xsl:text> </xsl:text>
<xsl:value-of select="name()"/><xsl:text>='</xsl:text>
<xsl:value-of select="."/>
<xsl:text>'</xsl:text>
</xsl:for-each>
<xsl:choose>
<xsl:when test="node() ¦ text()">
<!-- If there are children, close the start tag and
process the children -->
<xsl:text>&gt;</xsl:text>
<xsl:for-each select="node() ¦ text()">
<xsl:call-template name="outputTextNode" />
</xsl:for-each>
<!-- Emit the closing tag -->
<xsl:text>&lt;/</xsl:text>
<xsl:value-of select="name()" />
<xsl:text>&gt;</xsl:text>
</xsl:when>
<xsl:otherwise>
<!-- No children, emit the self-closing tag -->
<xsl:text>/&gt;</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>


</xsl:stylesheet>

Centering CSS-based layouts, Detecting text resize and more

The latest collection of links by Meryl Evans contains real gems:

MSXML firstChild property in VBScript

To complement a bit vague Microsoft MSDN document: firstChild property returns nothing (not null) if the XML node has no children. To test whether an XML node has children, use:
If Not (node.firstChild Is Nothing) Then ...
or, alternatively
If node.childNodes.length > 0 Then ...