XSLT: Get Element Name of "Tokenized Item" - xslt

Is there a way to gather element name of a tokenized value? I have been trying to do it but it is giving me an error "[Saxon-PE 9.6.0.7] XPTY0004: Required item type of first argument of name() is node(); supplied value has item type xs:string"
Here are my sample set of data:
<SET>
<REAL_TAGNAME> 1 2 3 4 </REAL_TAGNAME>
</SET>
If I have use this code:
<xsl:for-each select="SET/REAL_TAGNAME">
<xsl:for-each select="tokenize(normalize-space(.),'\s+')">
<Hardcode_Tag>
<xsl:value-of select="."/>
</Hardcode_Tag>
</xsl:for-each>
</xsl:for-each>
then I will successfully have the following:
<Hardcode_Tag>1</Hardcode_Tag>
<Hardcode_Tag>2</Hardcode_Tag>
<Hardcode_Tag>3</Hardcode_Tag>
<Hardcode_Tag>4</Hardcode_Tag>
But I want to move away from hard-coding and would like to use its original tag name to have something like:
<REAL_TAGNAME>1</REAL_TAGNAME>
<REAL_TAGNAME>2</REAL_TAGNAME>
<REAL_TAGNAME>3</REAL_TAGNAME>
<REAL_TAGNAME>4</REAL_TAGNAME>
While I try below with the xsl:element, it keeps giving me an error mentioned above:
<xsl:for-each select="SET/REAL_TAGNAME">
<xsl:for-each select="tokenize(normalize-space(.),'\s+')">
<xsl:element name="{name(.)}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
does anyone have any idea on how I can fix this? Thanks in advance for your help!

Within the xsl:for-each, the expression . refers to the current item, i.e. the current token extracted from the string value of the element. If you want to remember the name of the element that was the current element before you entered the for-each, just set a variable before the inner xsl:for-each:
<xsl:for-each select="SET/REAL_TAGNAME">
<xsl:variable name="element-name" select="name(.)"/>
<xsl:for-each select="tokenize(normalize-space(.),'\s+')">
<xsl:element name="{$element-name}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
In the code you show, of course, the value of $element-name will invariably be 'REAL_TAGNAME'. I'm assuming that's not true in the general case.

Related

XSLT / conditional for-each with same parent (uncle) node value

I need some help understanding how I can select only certain nodes having same parent (or uncle) element value. My XML looks like this
<shipment>
<goodslines>
<goodsline>
<position>1</position>
<packagenumbers>
<packagenumber>123</packagenumber>
</packagenumbers>
</goodsline>
<goodsline>
<position>1</position>
<packagenumbers>
<packagenumber>456</packagenumbers>
</packagenumbers>
</goodsline>
<goodsline>
<position>2</position>
<packagenumbers>
<packagenumber>789</packagenumbers>
</packagenumbers>
</goodsline>
</goodslines>
</shipment>
and the desired output would be:
123,456
789
So I would need to do for-each to "packagenumber" - level so, that it would take in consideration the "position" - element from upper level
The XSL might be something like this?
<xsl:for-each select="shipment/goodslines/goodsline[some condition here?]/packagenumbers/packagenumber">
<xsl:value-of select="current()"/>
<xsl:if test="not(position() = last())">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:text>
</xsl:text>
</xsl:for-each>
Any help would be appreciated.
If your xml always has the same structure you can use this:
<xsl:template match="goodsline[position = preceding-sibling::goodsline[1]/position]">
<xsl:text>,</xsl:text>
<xsl:value-of select="packagenumbers/packagenumber"/>
</xsl:template>
<xsl:template match="goodsline">
<xsl:text>
</xsl:text>
<xsl:value-of select="packagenumbers/packagenumber"/>
</xsl:template>
More specific templates (the one with the condition) will always hit first and have higher priority. There is probably another simple solution with for-each-group.

XSLT 1.0: if statement when position matches a variable value

I'm trying to insert an IF statement into an existing FOR-EACH loop to do something slightly different if it matches a variable from another node (the node I want is actually a sibling of it's parent - if that makes sense!?).
The value is a simple integer. I basically want to say: If the position is equal to the variable number then do XXXX.
Here is the XSLT, it's only v1.0 and not 2.0 that I can use.
<xsl:for-each select="/Properties/Data/Datum[#ID='ID1']/DCR[#Type='accordion_tab']/accordion_tab/sections">
<h3 class="accordionButton">
<xsl:if test="position()='openpane value to go here'">
<xsl:attribute name="class">
<xsl:text>new text</xsl:text>
</xsl:attribute>
</xsl:if>
</xsl:for-each>
My XML extract is here:
<sections>
<title>title</title>
<text>some text</text>
</sections>
<openpane>2</openpane>
You didn't make this clear in your question, but I assume you iterate over the sections elements in your for-each loop. From the for-each loop you can reach the openpane element by going through the parent of the current sections element:
<xsl:for-each select="sections">
<xsl:if test="position() = ../openpane">
...
</xsl:if>
</xsl:for-each>
You could also define a variable referring to the openpane element first:
<xsl:variable name="openpane" select="openpane"/>
<xsl:for-each select="sections">
<xsl:if test="position() = $openpane">
...
</xsl:if>
</xsl:for-each>

XSLT for-each counter - how to access data

for performance testing purposes I want to take a small XML file and create a bigger one from it - using XSLT. Here I plan to take each entity (Campaign node in the example below) in the original XML and copy it n times, just changing its ID.
The only way I can think of to realize this, is a xsl:for-each select "1 to n". But when I do this I do not seem to be able to access the entity node anymore (xsl:for-each select="campaigns/campaign" does not work in my case). I am getting a processor error: "cannot be used here: the context item is an atomic value".
It seems that by using the "1 to n" loop, I am loosing the access to my actual entity. Is there any XPath expression that gets me access back or does anyone have a completely different idea how to realize this?
Here is what I do:
Original XML
<campaigns>
<campaign id="1" name="test">
<campaign id="2" name="another name">
</cmpaigns>
XSLT I try to use
<xsl:template match="/">
<xsl:element name="campaigns">
<xsl:for-each select="1 to 10">
<xsl:for-each select="campaigns/campaign">
<xsl:element name="campaign">
<xsl:copy-of select="#*[local-name() != 'id']" />
<xsl:attribute name="id"><xsl:value-of select="#id" /></xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
</xsl:element>
</xsl:template>
Define a variable as the first thing in the match, like so:
<xsl:variable name="foo" select="."/>
This defines a variable $foo of type nodeset. Then access it like this
<xsl:for-each select="$foo/campaigns/campaign">
...
</xsl:for-each>

xslt assigning variable value to string literal

I have
<xsl:for-each select="ancestor-or-self::*">
<xsl:variable name="expression" select="name()" ></xsl:variable>
</xsl:for-each>
below I have href and i want to set the value this expression variable in href #######
Delete
I also tried with :
Delete
None of them worked.
Can Anybody help me out how to do that?
Basically my task is to find the expression for current node and send the expression for the same in href?
Adding More Info :
<br/><xsl:for-each select="ancestor-or-self::*">
<xsl:variable name="expression" select="name()" />
</xsl:for-each>
<b>Click Me</b>
when above xsl code is transformed it is giving below error:
Variable or parameter 'expression' is undefined.
In XSLT1.0, you could try setting the variable like so:
<xsl:variable name="expression">
<xsl:for-each select="ancestor-or-self::*">
<xsl:text>/</xsl:text>
<xsl:variable name="name" select="name()"/>
<xsl:value-of select="$name"/>
</xsl:for-each>
</xsl:variable>
So, assuming the following XML structure
<As>
<a>
<Bs>
<b>5</b>
</Bs>
</a>
<a>
<Bs>
<b>9</b>
</Bs>
</a>
<a/>
<a>
<Bs>
<b>12</b>
<b>14</b>
<b>15</b>
</Bs>
</a>
</As>
If you were positioned on the b element with the value of 14, then expression would be set to /As/a/Bs/b
However, this does not take into account multiple nodes with the same name, and so would not be sufficient if you wanted accurate XPath to select the node.
Instead, you could try the following:
<xsl:variable name="expression">
<xsl:for-each select="ancestor-or-self::*">
<xsl:text>/</xsl:text>
<xsl:variable name="name" select="name()"/>
<xsl:value-of select="$name"/>
<xsl:text>[</xsl:text>
<xsl:value-of select="count(preceding-sibling::*[name() = $name]) + 1"/>
<xsl:text>]</xsl:text>
</xsl:for-each>
</xsl:variable>
This would return /As[1]/a[4]/Bs[1]/b[2], which may be what you want.
Very simply, variables are scoped in XSL and exist only within the containing tag. Thus the variable named expression exists only within the for-each block. Also, variables can only be set once. Attempting to set a variable value a second time has no effect.
Therfore you have to declare the variable at or above the level where you want to use it, and put all the code to generate the value inside the variable declaration. If you can use XSLT2, the following will do what you want:
string-join(for $n in ancestor-or-self::* return name($n), '/')
I know it can also be done in XSLT 1 with a recursive template, but I don't have an example handy.

XSLT:How to deal with testing the value of an element?

I have an xml file in which there is tag namely, <Gender/> It carries either 'M' or 'F' as data, now my work is to test the value and write <Gender_Tag>Male</Gender_Tag> or <Gender_Tag>Female</Gender_Tag> according to the values M or F respectively .. I tried this code .. It used to work in other circumstances..
All relative paths expressed in a template are evaluated against the current node. Your template match Gender elements, so Gender='M' returns true if there is any Gender's child named 'Gender' with the value 'M'. I guess this is not the case...
Use the dot to express the current node (here a Gender element):
<xsl:template match="root/details/Gender">
<Gender_Tag>
<xsl:choose>
<xsl:when test=".='M'">
<xsl:text>Male</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>Female</xsl:text>
</xsl:otherwise>
</xsl:choose>
</Gender_Tag>
</xsl:template>
EDIT: You may use two templates too
<xsl:template match="root/details/Gender[.='M']">
<Gender_Tag>Male</Gender_Tag>
</xsl:template>
<xsl:template match="root/details/Gender[.='F']">
<Gender_Tag>Female</Gender_Tag>
</xsl:template>
<xsl:template match="root/details/Gender">
<xsl:choose>
<xsl:when test="normalize-space(text())='M'">
<Gender_Tag>Male</Gender_Tag>
</xsl:when>
<xsl:otherwise>
<Gender_Tag>Female</Gender_Tag>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
My example differs in two points from Scoregraphic's:
It uses xsl:choose to ensure, that only one Gender_Tag element is created (that also means, that if the text is not 'M', it is always a Female)
Use of normalize-space() strips white space around the text content of the element.
Untested, but may work...
<xsl:template match="root/details/Gender">
<xsl:if test="text()='M'">
<Gender_Tag>Male</Gender_Tag>
</xsl:if>
<xsl:if test="text()='F'">
<Gender_Tag>Female</Gender_Tag>
</xsl:if>
</xsl:template>
Without seeing XML its hard to be certain, but I think your sample XSLT should be:
<xsl:template match="root/details/Gender">
<xsl:if test=".='M'">
<Gender_Tag><xsl:text>Male</xsl:text></Gender_Tag>
</xsl:if>
<xsl:if test=".='F'">
<Gender_Tag><xsl:text>Female</xsl:text></Gender_Tag>
</xsl:if>
</xsl:template>
Use of choose as per another answer would be better (though I think it should be two explicit when clauses rather than a when and an otherwise)