Reputation: 27
I have a problem matching different identifiers in an XSL transformation from XML to HTML. I have a long list of link elements, like this :
<link target="#E_1 #FCB_1 #FWH_2 #FWH_3">
Each text beginning with # in the target attribute matches an xml:id attribute within a paragraph element like this :
<p xml:id="E_1">text text text</p>
<p xml:id="FCB_1">text text text</p>
<p xml:id="FWH_2">text text text</p>
<p xml:id="FWH_3">text text text</p>
What I need to do is to create a div element for each target attribute, meaning to obtain the following :
<div class="impair">
<div>
<p>Content of the paragraph with xml:id equal to "E_1"</p>
</div>
<div>
<p>Content of the paragraph with xml:id equal to "FCB_1"</p>
</div>
<div>
<p>Content of the paragraph with xml:id equal to "FWH_2"</p>
</div>
<div>
<p>Content of the paragraph with xml:id equal to "FWH_3"</p>
</div>
</div>
I have tried several things with xsl:variable, xsl:param, xsl:key or functions like starts-with or even substring but nothing works fine for now. So I am asking for help. I am still trying to improve my XSL skills... Many thanks in advance for your help. Flo
Upvotes: 0
Views: 288
Reputation: 70648
If you are looking for an XSLT 1.0 solution, here is one possible method, which converts the target
attribute to the string #E_1#FCB_1#FWH_2#FWH_3#
and uses various string functions to select the p
elements whose id
occurs in that string.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<xsl:apply-templates select="//link" />
</xsl:template>
<xsl:template match="link">
<xsl:variable name="target" select="concat(translate(@target, ' ', ''), '#')" />
<xsl:value-of select="$target" />
<div class="impair">
<xsl:apply-templates select="//p[contains($target, concat('#', @xml:id, '#'))]">
<xsl:sort select="string-length(substring-before($target, concat('#', @xml:id, '#')))" />
</xsl:apply-templates>
</div>
</xsl:template>
<xsl:template match="p">
<div>
<p><xsl:value-of select="." /></p>
</div>
</xsl:template>
</xsl:stylesheet>
This would not work if you had repeated ids in the target
attribute though, like #E_1 #FCB_1 #FWH_3 #FWH_3
. In this case, a recursively called named-template could work to split up the target
attribute:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:key name="p" match="p" use="concat('#', @xml:id)" />
<xsl:template match="/">
<xsl:apply-templates select="//link" />
</xsl:template>
<xsl:template match="link">
<div class="impair">
<xsl:call-template name="split">
<xsl:with-param name="string" select="@target" />
</xsl:call-template>
</div>
</xsl:template>
<xsl:template name="split">
<xsl:param name="string" />
<xsl:if test="$string != ''">
<xsl:apply-templates select="key('p', substring-before(concat($string, ' '), ' '))" />
<xsl:if test="contains($string, ' ')">
<xsl:call-template name="split">
<xsl:with-param name="string" select="substring-after($string, ' ')" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template match="p">
<div>
<p><xsl:value-of select="." /></p>
</div>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Reputation: 167716
Assuming an XSLT 2.0 processor you can use the id
function with the tokenized values from the target
attribute:
<xsl:template match="link[@target]">
<div class="impair">
<xsl:apply-templates select="id(for $idref in tokenize(@target, '\s+') return substring($idref, 2))" mode="wrap"/>
</div>
</xsl:template>
<xsl:template match="p" mode="wrap">
<div>
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</div>
</xsl:template>
Upvotes: 1