Lee Kelleher

Working with XSLT using new XML schema in Umbraco 4.1

Posted on . Estimated read time: 3 minutes (393 words)

Most of the Umbraco community are aware that the XML schema in the upcoming Umbraco 4.1 release has changed.

Instead of each document being a node element, the element name is the node-type alias, same with property values; they no longer use data elements with alias attributes. Here is a quick example, comparing the old/legacy with the new:

<node id="1066" parentID="-1" level="1" nodeName="Home" ... nodeTypeAlias="Homepage" path="-1,1066">
	<data alias="bodyText"><![CDATA[<p>Welcome to my homepage.</p>]]></data>
</node>

<Homepage id="1066" parentID="-1" level="1" ... nodeName="Home" path="-1,1066" isDoc="">
    <bodyText><![CDATA[<p>Welcome to my homepage.</p>]]></bodyText>
</Homepage>

Obviously for long-time Umbraco developers this will require a small shift in mindset, as we are way too familiar with writing XPath queries like;

$currentPage/descendant-or-self::node[string(data[@alias='umbracoNaviHide'])  != '1']

… which will need to be rewritten to;

$currentPage/descendant-or-self::*[umbracoNaviHide != '1']

Not that it's a difficult thing to change/update, but I can see a lot of questions being asked on the Our Umbraco forum.

Since there are a lot of existing Umbraco packages that use the current, (soon to be legacy) XML schema, it might be worthwhile making use of XSLT itself to convert the new back to the old – in order to keep the existing XSLT templates working.  Here's a quick example:

<!-- ROOT element -->
<xsl:template match="root">
	<xsl:element name="root">
		<xsl:apply-templates select="child::*" />
	</xsl:element>
</xsl:template>

<!-- NODE elements -->
<xsl:template match="*[count(@isDoc) = 1]">
	<xsl:element name="node">
		<xsl:attribute name="nodeTypeAlias">
			<xsl:value-of select="local-name()"/>
		</xsl:attribute>
		<xsl:copy-of select="@@*" />
		<xsl:apply-templates select="child::*" />
	</xsl:element>
</xsl:template>

<!-- DATA elements -->
<xsl:template match="*[count(parent::*) > 0 and count(@isDoc) = 0]">
	<xsl:element name="data">
		<xsl:attribute name="alias">
			<xsl:value-of select="local-name()"/>
		</xsl:attribute>
		<xsl:copy-of select="text()" />
	</xsl:element>
</xsl:template>

These templates would be used to transform the new schema/structure back to the old legacy schema/structure, like so:

<xsl:variable name="legacyFragment">
	<xsl:apply-templates select="$currentPage/ancestor::root" />
</xsl:variable>
<xsl:variable name="legacyRoot" select="msxml:node-set($legacyFragment)/root" />
<xsl:variable name="legacyCurrentPage" select="$legacyRoot/descendant-or-self::node[@id = $currentPage/@id]" />

Since you can't modify a variable's value in XSLT, it would be a case of replacing all the “$currentPage” references with “$legacyCurrentPage” (or whatever you decided to call it). But in all honesty, if you are going to start modifying your XSLT, then it would be better to refactor the XPath statements to use the new schema!

Personally, I'm looking forward to using the new XML schema in Umbraco 4.1, the structure makes more sense from a semantic perspective – and I am told it will have performance gains on the XSLT processor.