Reputation: 2066
A global variable indicates, for each of various kinds of elements, what attributes need to be processed.
<xsl:variable name="attributes.rtf">
<element name="measure">
<att>type</att>
<att>quantity</att>
<att>unit</att>
</element>
<element name="milestone">
<att>n</att>
</element>
<element name="lb">
<att>ed</att>
<att>type</att>
<att>subtype</att>
<att>n</att>
</element>
</xsl:variable>
<xsl:variable name="attributes" select="exslt:node-set($attributes.rtf)" /> <!-- This is XSLT 1.0 -->
When the context is an element, I need a for-each that will loop through attributes for that kind of element. I don't see how to do this in one XPath expression. Right now I have these two:
<xsl:variable name="temp" select="." /> <!-- For, say, a <measure> element, -->
<xsl:for-each select="$attributes/element[@name=name($temp)]/att"> <!-- loop through the names "type", "quantity", and "unit" -->
<xsl:for-each select="$temp/@*[name()=current()]"> <!-- If an attribute of that name exists in the current <measure> element -->
<!-- (now stored as $temp), then process that attribute. -->
</xsl:for-each>
</xsl:for-each>
Is there a way to do this in one expression, without creating $temp.
But also, I need an outer test condition that indicates whether the context element has any of the listed attributes at all. For that test, I'd like one expression.
(Processing of the attributes does need to be ordered, so maybe that requires the two loops. But order would not be relevant in the test condition. So maybe just that could be done with one expression.)
Upvotes: 2
Views: 399
Reputation: 243579
I. The "simple" problem:
When the context is an element, I need a for-each that will loop through attributes for that kind of element.
No xsl-for-each
is needed.
I don't see how to do this in one XPath expression.
Just use:
<xsl:apply-templates select=
"@*[name()=$attributes/element[@name=name(current())]/att]"/>
Explanation:
Proper use of the current()
function.
II. The "complex" problem:
Processing of the attributes does need to be ordered, so maybe that requires the two loops
Here is a complete example showing that no other nested <xsl:apply-templates>
is necessary to produce the attributes in the order specified in $attributes
:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="attributes.rtf">
<element name="measure">
<att>type</att>
<att>quantity</att>
<att>unit</att>
</element>
<element name="milestone">
<att>n</att>
</element>
<element name="lb">
<att>ed</att>
<att>type</att>
<att>subtype</att>
<att>n</att>
</element>
</xsl:variable>
<xsl:variable name="attributes" select="exslt:node-set($attributes.rtf)" />
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="t/*">
<xsl:copy>
<xsl:apply-templates select="$attributes/element[@name=name(current())]/att">
<xsl:with-param name="pCurrent" select="current()"/>
</xsl:apply-templates>
<xsl:apply-templates select=
"@*[not(name() = $attributes/element[@name=name(current())]/att)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="att">
<xsl:param name="pCurrent" select="/.."/>
<xsl:if test="$pCurrent[@*[name()=current()]]">
<xsl:attribute name="{.}">
<xsl:value-of select="concat(.,'-',.)"/>
</xsl:attribute>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document (none was provided!!!):
<t>
<lb type="normal" ed="uni" n="3"
subtype="initial" other="yes"/>
<milestone n="2" other="yes"/>
<measure quantity="3" unit="cm"
type="length" other="yes"/>
</t>
the wanted, correct result is produced:
<t>
<lb ed="ed-ed" type="type-type" subtype="subtype-subtype" n="n-n" other="yes"/>
<milestone n="n-n" other="yes"/>
<measure type="type-type" quantity="quantity-quantity" unit="unit-unit" other="yes"/>
</t>
Upvotes: 2