Subject: Re: [xsl] concatenation of sibling names From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx> Date: Tue, 12 Feb 2002 18:03:08 +0000 |
Hi Saverio, > Is it possible to concatenate the names of all siblings of a node > into a string for later use? It depends what you mean by "for later use". You can assign a variable a string that is the concatenation of the names of all siblings of a node fairly easily - whether you can use that variable at the location you want to use the variable depends on where you create the variable and where you want to use the variable. You can't, for example, create the variable in one template and then use it in another template. (Though you can pass the value of the variable through to a later template explicitly by using parameters.) To get all the siblings of a node, you can create a union of its preceding siblings and its following siblings: preceding-sibling::* | following-sibling::* You can iterate over those with an xsl:for-each, and for each of them give their name: <xsl:for-each select="preceding-sibling::* | following-sibling::*"> <xsl:value-of select="name()" /> </xsl:for-each> Actually, it looks like you want the names to be separated by spaces, so you need to add a space after all but the last name: <xsl:for-each select="preceding-sibling::* | following-sibling::*"> <xsl:value-of select="name()" /> <xsl:if test="position() != last()"> </xsl:if> </xsl:for-each> You can assign the result of that xsl:for-each to a variable by including the xsl:for-each within the variable: <xsl:variable name="sibling-names"> <xsl:for-each select="preceding-sibling::* | following-sibling::*"> <xsl:value-of select="name()" /> <xsl:if test="position() != last()"> </xsl:if> </xsl:for-each> </xsl:variable> Technically, this variable holds a result tree fragment, but if you use it like a string then it will behave in the same way as a string, so that technicality doesn't matter much in practice. --- According to the WDs, in XSLT 2.0, you could do: <xsl:variable name="sibling-names"> <xsl:value-of separator=" " select="for $s in (preceding-sibling::* | following-sibling::*) return name($s)" /> </xsl:variable> Oddly enough, I can't think of a way to explicitly create a string in one easy move, however. (And this is a bit more important in XSLT 2.0, where data types do really matter.) What you'd expect is a concat-sequence() function, taking a sequence of strings and returning the concatenation of those strings. Although actually things wouldn't be particularly pretty even if you did have such a thing... I *think* you would have to do one of the following: <!-- create a text node holding the string, using xsl:value-of and it's helpful separator attribute --> <xsl:variable name="sibling-names-text-node"> <xsl:value-of separator=" " select="for $s in (preceding-sibling::* | following-sibling::*) return name($s)" /> </xsl:variable> <!-- convert the text node to a string with the string() function --> <xsl:variable name="sibling-names" select="string($sibling-names-text-node)" /> Or: <!-- create a sequence of, for each sibling, their name followed by a space; take all but the last item in that sequence, and concatenate the items together into a string with the (putative) concat-sequence() function --> <xsl:variable name="sibling-names" select="concat-sequence( (for $s in (preceding-sibling::* | following-sibling::*) return (name($s), ' ')) [position() != last()])" /> Or: <!-- hold the siblings in a variable to make the processing easier --> <xsl:variable name="siblings" select="preceding-sibling::* | following-sibling::*" /> <!-- create the sibling names by concatenating together the items in the sequence consisting of all the names of the siblings followed by a space (or an empty string for the last of the siblings) --> <xsl:variable name="sibling-names" select="concat-sequence( for $i in 1 to count($siblings) return (name($siblings[$i]), if ($i != count($siblings)) then ' ' else '')" /> The first is messy, because you have to do it in two steps. The second is all right, but a little counter-intuitive. The third demonstrates how tricky it is to iterate over a sequence with the for expression (as currently defined) if the position of an item in the expression is actually an important piece of information. The idea of a for expression that uses the context node rather than a range variable (or a simple mapping operator) really helps in this situation. If you *didn't* have range variables, it would limit the flexibility of the for expression (because you couldn't do joins), but it would make this task a lot easier because you could do: <xsl:variable name="sibling-names" select="concat-sequence( for (preceding-sibling::* | following-sibling::*) return (name(), if (position() != last()) then ' ' else ''))" /> The changes to XSLT that I favour would (a) use a simple mapping operator rather than a for expression [it's just a different syntax, but I think it's a handy one] and (b) enable you to assign values other than (new) node sets to variables using their content. Ideally, I'd like it if you could make $sibling-names hold a string with just: <xsl:variable name="sibling-names" type="xs:string"> <xsl:value-of separator=" " select="(preceding-sibling::* | following-sibling::*) => name()" /> </xsl:variable> But in any case, it sounds like a concat-sequence() function would be useful. Cheers, Jeni --- Jeni Tennison http://www.jenitennison.com/ XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
[xsl] concatenation of sibling name, Saverio Perugini | Thread | Re: [xsl] concatenation of sibling , Peter Davis |
Re: [xsl] How to assign a value and, Peter Davis | Date | Re: [xsl] PDF security, Peter Davis |
Month |