Re: [xsl] Identifying unique attribute values in nested sibling elements

Subject: Re: [xsl] Identifying unique attribute values in nested sibling elements
From: Brandon Ibach <brandon.ibach@xxxxxxxxxxxxxxxxxxx>
Date: Wed, 5 Oct 2011 16:15:47 -0400
To go a bit beyond Ken's suggestions, here's a very non-C++ style of
approach, relying on the data to drive the processing, which XSLT
supports very well.  This assumes that you only have a single Value
element in each Stamp.  Your code seemed to indicate that this was the
case and your use of <xsl:for-each> seemed to be mostly for purposes
of shifting your context, but if there's a chance you might have more
than one Value, that would make for some strange results, so it's
something to look out for, if you don't have a schema to enforce such
rules on your input.

Hopefully, this isn't too hard to follow.  I have a weakness for
C-cubed code (concise, clever and, all too often, a bit cryptic).
Besides the more content-driven style, the other main feature here is
the simplification allowed by the fact that in all cases, your code
was generating an <a class="button">, so you may as well only generate
it once (the "DRY" rule works in any sort of programming, after all,
regardless of the language used).

 <xsl:function name="cps:report">
   <xsl:param name="Stamp"/>
   <xsl:variable name="file">
     <xsl:apply-templates select="$Stamp/Value" mode="cps:href"/>
   </xsl:variable>
   <a class="button" href="../aval/{$file}.htm">
     <xsl:apply-templates select="$Stamp/Value" mode="cps:text"/>
   </a>
 </xsl:function>

 <xsl:template match="Value[@l-value]" mode="cps:href">
   <xsl:value-of select="lower-case(@l-value)"/>
 </xsl:template>
 <xsl:template match="Value[not(@l-value)]" mode="cps:href">
   <xsl:value-of select="concat((@kc-value, 0)[1], '-')"/>
   <xsl:value-of select="(@h-value, 0)[1]"/>
 </xsl:template>

 <xsl:template match="Value[@l-value]" mode="cps:text">
   <xsl:value-of select="@l-value"/>
 </xsl:template>
 <xsl:template match="Value[not(@l-value)]" mode="cps:text">
   <xsl:variable name="kc" select="@kc-value[. ne '0']"/>
   <xsl:variable name="h" select="@h-value[. ne '0']"/>
   <xsl:value-of select="concat($kc, '.'[$kc][$h], $h)"/>
   <xsl:value-of select="if ($kc) then 'Kc' else 'h'"/>
 </xsl:template>

-Brandon :)


On Tue, Oct 4, 2011 at 10:23 AM, Mark <mark@xxxxxxxxxxxx> wrote:
> Hi Ken,
> Despite my confused presentation of the problem last week, you supplied me
> with two functions that obtain the unique attributes from B <Value> element
> within a set of <Stamp> elements. Works beautifully, but I did make some
> modifications to the second function. Please let me know if my
modifications
> are consistent with the idiom you have been talking about or if I have
> slipped back into C++. Any improvements would be greatly appreciated if and
> when you have the time and desire. The h-value and l-value were not
> presented to you as part of the original use case.
>
> They do exactly what I wanted.
> Thanks,
> Mark
>
> the XML with the B output:
> <Value kc-value="1"> B  B  B  B  B  B  B  B  B  B  B  B Text: 1kc B  B 
File: 1-0.htm
> <Value kc-value="1" h-value="50"> Text 1.50kc File: 1-50.htm
> <Value kc-value="0" h-value="50"> Text: 50h B File: 0-50.htm
> <Value kc-value="0" l-value="A" B  B  Text: A B File:a.htm
>
> Only the second function, cps:report, has been modified:
>
> <!--compose a string of attribute names and their values;
> B  avoid the possibility of co-incidental string values;-->
> B <xsl:function name="cps:attrs">
> B  <xsl:param name="this"/>
> B  <xsl:value-of>
> B  B  <xsl:for-each select="$this/@*">
> B  B  B  <xsl:sort select="name(.)"/>
> B  B  B  <xsl:value-of select="name(.),'&#xfdd0;',.,'&#xfdd0;'"/>
> B  B  </xsl:for-each>
> B  </xsl:value-of>
> B </xsl:function>
>
> B <!--common reporting-->
> B <xsl:function name="cps:report">
> B  <xsl:param name="Stamp"/>
> B  <xsl:for-each select="$Stamp">
> B  B  <!-- l-value -->
> B  B  <xsl:choose>
> B  B  B  <xsl:when test="Value/@l-value">
> B  B  B  B  <xsl:for-each select="Value/@l-value">
> B  B  B  B  B  <a class="button" href="../aval/{lower-case(.)}.htm">
> B  B  B  B  B  B  <xsl:value-of select="."/>
> B  B  B  B  B  </a>
> B  B  B  B  </xsl:for-each>
> B  B  B  </xsl:when>
> B  B  B  <!-- kc-value + h-value -->
> B  B  B  <xsl:when test="Value/@h-value">
> B  B  B  B  <xsl:variable name="h">
> B  B  B  B  B  <xsl:for-each select="Value/@h-value">
> B  B  B  B  B  B  <xsl:value-of select="."/>
> B  B  B  B  B  </xsl:for-each>
> B  B  B  B  </xsl:variable>
> B  B  B  B  <xsl:for-each select="Value/@kc-value">
> B  B  B  B  B  <xsl:choose>
> B  B  B  B  B  B  <xsl:when test="not(. eq '0')">
> B  B  B  B  B  B  B  <a class="button" href="../aval/{concat(., '-',
$h)}.htm">
> B  B  B  B  B  B  B  B  <xsl:value-of select="concat(., '.', $h)"/>
> B  B  B  B  B  B  B  B  <xsl:text>KD
</xsl:text>
> B  B  B  B  B  B  B  </a>
> B  B  B  B  B  B  </xsl:when>
> B  B  B  B  B  B  <xsl:otherwise>
> B  B  B  B  B  B  B  <a class="button" href="../aval/{concat(., '-',
$h)}.htm">
> B  B  B  B  B  B  B  B  <xsl:value-of select="$h"/>
> B  B  B  B  B  B  B  B  <xsl:text>h</xsl:text>
> B  B  B  B  B  B  B  </a>
> B  B  B  B  B  B  </xsl:otherwise>
> B  B  B  B  B  </xsl:choose>
> B  B  B  B  </xsl:for-each>
> B  B  B  </xsl:when>
>
> B  B  B  <xsl:otherwise>
> B  B  B  B  <!-- Only when there is no h-value B -->
> B  B  B  B  <xsl:for-each select="Value/@kc-value">
> B  B  B  B  B  <a class="button" href="../aval/{concat(., '-0')}.htm">
> B  B  B  B  B  B  <xsl:value-of select="."/>KD
<xsl:text/>
> B  B  B  B  B  </a>
> B  B  B  B  </xsl:for-each>
> B  B  B  </xsl:otherwise>
> B  B  </xsl:choose>
> B  </xsl:for-each>
> B </xsl:function>

Current Thread