Entity Tricks for Your XPaths (Part II)

Written by .
Got comments? I’m @greystate on Twitter.
Didn’t read Part I? Go read that first then.

This is Part II of the venture into using entities for more than just the odd danish character here and there :-)

If you recall what happened in Part I, we learned how to declare and use our own General Entities inside an XSLT file, to collapse some of the more verbose XPath stuff into something a little more meaningful. By later changing the entity declarations we could swap in support for the new XML format in Umbraco 4.1, while keeping the wording of the XSLT intact.

If you’re anything like me, you’ve been hearing the same song inside your head since then:

“Please, please; I don’t wanna write this in all my XSLTs…”

(Ooops, very cheesy. Sorry ‘bout that)

The problem is that if (which of course means when) we need to change the entity declarations, we’ll have to change them in every file anyway, so we don’t really gain that much do we? Unless… we’re able to separate the entity definitions from the actual files, and make sure there’s only one place we have to change them, if needed.

There is another kind of entity hidden in the old dusty DTD book: The External Parameter Entity — an entity type that’s allowed inside a DTD, which we can put to good use here. It’s declared like this:

<!ENTITY % entities SYSTEM "entities.dtd">

— and we use it like this:

<!DOCTYPE xsl:stylesheet [
    <!ENTITY % entities SYSTEM "entities.dtd">

    %entities;
]>

The file “entities.dtd” contains the entities we defined in Part I:

<!ENTITY nbsp "&#00A0;">
<!ENTITY node "node">
<!ENTITY hidden "data[@alias = 'umbracoNaviHide' = 1]">

You can probably already see where this is going, but I’m going to explain it nevertheless.

So now the “normal” entities from the “entities.dtd” file are available for use inside the XSLT (or XML) file.

To continue the story where we’d like to reuse our existing (pre 4.1beta2) XSLT macros on a new 4.1 installation, we could define a set of entities that would enable us to write version-independent XSLT (i.e., using &hidden; and &node; but also &NewsItem; etc.) and put them in a separate file, e.g.: "entities.oldskool.dtd":

<!ENTITY nbsp "&#00A0;">
<!ENTITY node "node">
<!ENTITY hidden "data[@alias = 'umbracoNaviHide' = 1]">
<!ENTITY NewsItem "node[@nodeTypeAlias = 'NewsItem']">

We’ll then create another file — "entities.newskool.dtd" with the “translated” versions of these:

<!ENTITY nbsp "&#00A0;">
<!ENTITY node "*[@isDoc]">
<!ENTITY hidden "umbracoNaviHide = 1">
<!ENTITY NewsItem "NewsItem">

and simply swap the filename in the Parameter Entity definition when it’s time to go newskool!

Caveat

Before you start deploying this technique big-time, you’ll need to know what happens if you’re using .NET and the compiled transforms it supplies. In other words: Are you using Umbraco? — then you should read this:

If you change the external entity file, you have to Save or touch the XSLT file(s) that use it, before the changes take effect!

—This is a known issue which also bites you if you’re using xsl:include or xsl:import to externalize some of your commonly used templates (and that’s a future article, right there).

I’m sure, there’s someone, somewhere, working on being the first Ninja to create the package for Umbraco that adds a “Touch all XSLT files NOW” button to the Developer Section… huh? Beer’s on me if you do.

Offroadcode already made a super-easy solution for this: Check out XSLTouch at their website, or grab the Umbraco Package on our.umbraco.org. - Guess I owe you a beer Pete :-)

That’s all for Part II — I’ll finish off in Part III with a longer example of how I tie all this together, when developing with XML & XSLT.