To migrate my photo albums from Gallery to WordPress, I ended up having to transform XML fragments such as:
<row> <field name="g_id">22909</field> <field name="g_description" xsi:nil="true" /> <field name="g_keywords" xsi:nil="true" /> <field name="g_summary" xsi:nil="true" /> <field name="g_title">dsc00001</field> <field name="g_pathComponent">aaa.jpg</field> <field name="g_pathComponent">011120-Forum-XML-2001</field> <field name="g_orderWeight">1000</field> </row>
Into SQL statements such as:
update wp_ngg_gallery g, wp_ngg_pictures p set p.image_slug = "dsc00001", p.description = "dsc00001", p.alttext = "dsc00001", p.sortorder = 1 where p.galleryid = g.gid and g.title = "011120-Forum-XML-2001" and p.filename = "aaa.jpg";
That’s pretty obvious to do, but the usual technique is both verbose, boring and unreadable, giving something more or less like this:
<xsl:template match="row"> <xsl:variable name="title" select="if (field[@name='g_title'] != '') then field[@name='g_title'] else field[@name='g_pathComponent'][1]"/> <xsl:variable name="description" select="if (field[@name='g_description'] != '') then field[@name='g_description'] else $title"/> <xsl:variable name="summary" select="if (field[@name='g_summary'] != '') then field[@name='g_summary'] else $title"/> <xsl:text><![CDATA[update wp_ngg_gallery g, wp_ngg_pictures p set p.image_slug = "]]></xsl:text> <xsl:value-of select="$title"/> <xsl:text><![CDATA[", p.description = "]]></xsl:text> <xsl:value-of select="$description"/> <xsl:text><![CDATA[", p.alttext = "]]></xsl:text> <xsl:value-of select="$summary"/> <xsl:text><![CDATA[", p.sortorder = ]]></xsl:text> <xsl:value-of select="position()"/> <xsl:text><![CDATA[ where p.galleryid = g.gid and g.title = "]]></xsl:text> <xsl:value-of select="field[@name='g_pathComponent'][2]"/> <xsl:text><![CDATA[" and p.filename = "]]></xsl:text> <xsl:value-of select="field[@name='g_pathComponent'][1]"/> <xsl:text><![CDATA["; ]]></xsl:text> </xsl:template>
Half way through typing this awful tag soup, I wished I could use some string formatting feature such as what I would have done in Python:
print '''
update
wp_ngg_gallery g,
wp_ngg_pictures p
set
p.image_slug = "%(title)",
p.description = "%(description)",
p.alttext = "%(alttext)",
p.sortorder = %(sortOrder)
where
p.galleryid = g.gid
and g.title = "%(galleryTitle)"
and p.filename = "%(filename)";
''' % {"title": ..., "description: ...", ...}
Much better, don’t you agree?
The good news is that it’s easy to implement in XSLT 2.0!
I have chosen to use a « ${param-name} » syntax rather than the Pythonic « %(param-name) » but you could easily adapt the implementation to stick to the Pythonic syntax and the code is as simple as defining this named template:
<xsl:template name="template">
<xsl:param name="template"/>
<xsl:param name="parameters"/>
<xsl:analyze-string select="$template" regex="\$\{{(\i\c*)\}}" flags="">
<xsl:matching-substring>
<xsl:value-of select="$parameters/*[name() = regex-group(1)]"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
Having done so, I can now use it to format my string:
<xsl:variable name="title"
select="if (field[@name='g_title'] != '') then field[@name='g_title'] else field[@name='g_pathComponent'][1]"/>
<xsl:variable name="description" select="if (field[@name='g_description'] != '') then field[@name='g_description'] else $title"/>
<xsl:variable name="summary" select="if (field[@name='g_summary'] != '') then field[@name='g_summary'] else $title"/>
<xsl:call-template name="template">
<xsl:with-param name="parameters">
<title>
<xsl:value-of select="$title"/>
</title>
<description>
<xsl:value-of select="$description"/>
</description>
<alttext>
<xsl:value-of select="$summary"/>
</alttext>
<gallery-title>
<xsl:value-of select="field[@name='g_pathComponent'][2]"/>
</gallery-title>
<filename>
<xsl:value-of select="field[@name='g_pathComponent'][1]"/>
</filename>
<sort-order>
<xsl:value-of select="position()"/>
</sort-order>
</xsl:with-param>
<xsl:with-param name="template"><![CDATA[update
wp_ngg_gallery g,
wp_ngg_pictures p
set
p.image_slug = "${title}",
p.description = "${description}",
p.alttext = "${alttext}",
p.sortorder = ${sort-order}
where
p.galleryid = g.gid
and g.title = "${gallery-title}"
and p.filename = "${filename}";
]]></xsl:with-param>
</xsl:call-template>
Much nicer, don’t you think so?
One thought on “String Formatting in XSLT 2.0”