Lee Kelleher

Working with XSLT using new XML schema in Umbraco 4.1

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::*) &gt; 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.


  • http://www.prolificnotion.co.uk Simon Dingley

    There is a flag in the umbracoSettings.config file for 4.1 called UseLegacyXmlSchema to allow you to continue using the current < 4.1 XML schema which I would guess makes the above workaround redundant. That said, the post helped me to locate a problem with my XSLT in 4.1 caused by the new schema so thanks.