It sounds easy, but none of my "easy" syntax worked:
<xsl:param name="length"/>
<xsl:attribute name="width">$length</xsl:attribute>
not
<xsl:attribute name="width"><xsl:value-of-select="$length"></xsl:attribute>
any suggestions?
thanks
<xsl:attribute
name="width">$length</xsl:attribute>
This will create an attribute with value the string $length. But you want the value of the xsl:param named $length.
<xsl:attribute
name="width"><xsl:value-of-select="$length"></xsl:attribute>
Here the <xsl:value-of> element is not closed -- this makes the XSLT code not well-formed xml.
Solution:
Use one of the following:
<xsl:attribute name="width"><xsl:value-of select="$length"/></xsl:attribute>
or
<someElement width="{$length}"/>
For readability and compactness prefer to use 2. above, whenever possible.
You probably don't even need xsl:attribute here; the simplest way to do this is something like:
<someElement width="{$length}" ... >...</someElement>
Your first alternative fails because variables are not expanded in text nodes. Your second alternative fails because you attempt to call <xsl:value-of-select="...">, while the proper syntax is <xsl:value-of select="..."/>, as described in the section Generating Text with xsl:value-of in the standard. You can fix your code by using
<xsl:attribute name="width"><xsl:value-of select="$length"/></xsl:attribute>
or, as others have noted, you can use attribute value templates:
<someElement width="{$length}" ... >...</someElement>
Related
I'm trying to reformat an XML I get from an appliance into an HTML table, and it's format is not usual.
It use unique references in node name's, like this:
/network/content/host/content/REF_1/content
/network/content/network/content/REF_2/content
and then, it use the same references to another part of the file, as a value of a content node, like this:
/rules/content/rules/content/REF_3/content/sources/content/name = REF_1
/rules/content/rules/content/REF_3/content/destinations/content/name = REF_2
I'm trying to write a template for content that instead of getting me REF_ID which is unique, I try to get the name, in the other branch leaf. this mean I'm trying to find a value that is out of my actual context.
I'm able to retrieve the name XPATH using this variable:
<xsl:variable name='objName' select="concat('/storage/objects/',#linkclass,'/content/',#linktype,'/content/',current(),'/content/name/content')" />
but, I'm not able to use this XPATH in a query like:
<xsl:value-of select="{$objName}">
I suppose this doesn't work because it's out of context but when I ask statically for one of those XPATH I get the value.
My full code is not very complicated:
<xsl:template match="content">
<xsl:variable name='objXPATH' select="concat('/storage/objects/',#linkclass,'/content/',#linktype,'/content/',current(),'/content/name/content')" />
<xsl:variable name='obj' select="{$objXPATH}" />
<xsl:element name="a">
<xsl:attribute name="href">
#<xsl:value-of select="."/>
</xsl:attribute>
<xsl:value-of select="$obj"/>
<br />
</xsl:element>
</xsl:template>
I need help to fix this, I'm on it since one day with no evolution, and it's driving me crazy. I'm more like a script kiddie than a real developer.
Dynamic evaluation (treating a string in a variable as an XPath expression and evaluating it) is available as a vendor extension in a number of XSLT processors, and it becomes part of the standard with the introduction of xsl:evaluate in XSLT 3.0. If your XSLT processor doesn't have such an extension you may be able to write it yourself. Alternatively, if you explain the problem better, we may be able to suggest a solution that does not require dynamic evaluation.
XSLT2.0 seems to allow declaring key inline, inside the <key> element.
All the examples I have seen declare an intermediate XML fragment and match on that, using #use. I think that is wasteful.
Can you please provide an example of a XSLT 2.0 key declaration using sequence constructor inside the key element rather than #use?
Usually the value that you want to index is a very simple function of the objects being indexed, so the #use attribute works perfectly well. You can use a contained sequence constructor for more complex cases if you need to, but I've very rarely seen it needed. For example you might want to index sections by their section number like this:
<xsl:key name="k" match="section">
<xsl:number level="multi" count="section" format="1.1.1"/>
</xsl:key>
I don't know what makes you think that using the #use attribute is "wasteful".
I don't think I have used that feature so far and I can't think of a good sample for an obvious use case but let's assume foo elements have some value child elements and we want to sort the value elements and only key on the first or last few in sort order so we could use e.g.
<xsl:key name="by-first-three-values" match="foo">
<xsl:for-each select="value/xs:decimal(.)">
<xsl:sort select="."/>
<xsl:if test="position() lt 4">
<xsl:sequence select="."/>
</xsl:if>
</xsl:for-each>
</xsl:key>
Of course you could avoid that use by writing a function that sorts with perform-sort and then call that function in use="mf:sort(value)[position() lt 4]" but I guess there is at least the flexibility to do it inline of the xsl:key.
What I am after is even more simple, something similar to:
<xsl:key name="AcronymKey" match="a:acronymItem" use="a:acronym"/>
<xsl:template name="AcronymnStandsFor">
<xsl:param name="acronym"/>
<!-- change context to current document so the key will work -->
<xsl:for-each select="document('')">
<xsl:value-of select="key('AcronymKey',$acronym)/a:standsFor"/>
</xsl:for-each>
</xsl:template>
<a:acronymList>
<a:acronymItem>
<a:acronym>Ant</a:acronym>
<a:standsFor>Another Neat Tool</a:standsFor>
</a:acronymItem>
</a:acronymList>
But where the actual key is inside the key element. Is that possible, given the syntax?
I am using XSLT 2.0 to transform some XML. The source XML looks similar to this:
<AnimalTest>
<AnimalTypes>
<AnimalType name="cat"/>
<AnimalType name="dog"/>
</AnimalTypes>
<Animals>
<Animal name="Sylvester" typeName="cat"/>
<Animal name="Fido" typeName="dog"/>
<Animal name="Tom" typeName="cat"/>
</Animals>
</AnimalTest>
Inside the XSL template to handle AnimalType tags, I want to use the name attribute of the AnimalType inside an XPath expression. The only way I have been able to achieve this, is by introducing a variable that holds the attribute #name and is referred from inside the XPath expression, like this:
<xsl:template match="AnimalType">
<xsl:variable name="typename" select="#name"/>
<xsl:apply-templates select="/AnimalTest/Animals/Animal[#typeName=$typename]"/>
</xsl:template>
This works, but I wonder whether I really have to use this temporary variable. Is there any better way to refer to that #name attribute? It looks like a detour to me.
If you really disliked using the variable, you could use the current() function to refer to the current context node (AnimalType in your case)
<xsl:apply-templates select="/AnimalTest/Animals/Animal[#typeName=current()/#name]"/>
If you had a more complex expression, using a variable can improve readability though, and you could potentially re-use in other places.
One thing to note is that his declaration
<xsl:variable name="typename" select="#name"/>
Is not quite the same as this declaration
<xsl:variable name="typename">
<xsl:value-of select="#name" />
</xsl:variable>
Although both variables will contain the same value. In the latter case (using xsl:value-of) you are creating a copy of the value of the name attribute. In the former case, you are referring to the attribute directly. Therefore using the latter format would be less efficient.
As a slight aside, you may consider using a key here to look up your Animal elements by their typeName
<xsl:key name="AnimalByType" match="Animal" use="#typeName" />
That way, your apply-templates expression can be simplified to just the following
<xsl:template match="AnimalType">
<xsl:apply-templates select="key('AnimalByType', #name)"/>
</xsl:template>
More for reference than actual need: what is the XPath syntax to allow me to reference an element in a xsl:for-each block when the same element name is used elsewhere?
Please note, unfortunately this must be a 1.0 solution
For example, I have the following simple XML, and I want to match up the items with the same id value...
<data>
<block1>
<item><id>1</id><text>Hello</text></item>
<item><id>2</id><text>World</text></item>
</block1>
<block2>
<item><id>1</id><text>123</text></item>
<item><id>2</id><text>ABC</text></item>
</block2>
</data>
If I have a for-each on the block1, how can I reference both the id within the block1 and the id within the block2?
This will work, but I think it is messy...
<xsl:for-each select="//block1/item">
<xsl:variable name="id" select="id"/>
<xsl:value-of select="text"/> - <xsl:value-of select="//block2/item[id=$id]/text"/>
</xsl:for-each>
With the result of...
Hello - 123
World - ABC
Is there a simplified way of replacing the $id in select="//block2/item[id=$id]/text" so that it is referring to the id element from the for-each?
Another way to do it which you may find clearer, and will probably be faster, is to use keys:
<xsl:key name="b2" match="block2/item" use="id"/>
then
<xsl:value-of select="key('b2', id)/text"/>
What you have is correct and common as it is. There's no need to simplify it further; it's a standard idiom recognized and used by those working with XSLT.
I am parsing a document, with different behavior depending on whether the id attribute is an element of a collection of values ($item-ids in the code below). My question is, why do I need to assign a variable and then compare with that value, like this:
<xsl:template match="word/item">
<xsl:variable name="id" select="#abg:id"/>
<xsl:if test="$item-ids[.=$id]">
<xsl:message>It matches!</xsl:message>
</xsl:if>
</xsl:template>
It seems to be that I should be able to do it like this, though it doesn't work:
<xsl:template match="word/item">
<xsl:if test="$item-ids[.=#abg:id]">
<xsl:message>It matches!</xsl:message>
</xsl:if>
</xsl:template>
This is something I keep forgetting and having to relearn. Can anybody explain why it works this way? Thanks.
To understand XPath, you need to understand the concept of the context node. An expression like #id is selecting an attribute of the context node. And the context node changes inside square brackets.
You don't have to use a variable in this case. Here you can use:
<xsl:template match="word/item">
<xsl:if test="$item-ids[. = current()/#abg:id]">
<xsl:message>It matches!</xsl:message>
</xsl:if>
</xsl:template>
The reason you can't just use $item-ids[. = #abg:id] is that inside the [], you are in the context of whatever is right before the [] (in this case $item-ids), so #abg:id would be treated as $item-ids/#abg:id, which isn't what you want.
current() refers to the current context outside of the <xsl:if> so current()/#abg:id should reflect you the value you want.
I think it's because the line
<xsl:if test="$item-ids[.=#abg:id]">
compares the value of $item-ids to the string '#abg:id' - you need to compare it to the value of #abg:id which is why you need to select that value into the $id variable for the test to work.
Does that help at all?
Edit: I've misunderstood the issue - the other answers are better than mine.