XSLT use value-of inside select (Nested value-of select) - xslt

I am trying to pass the result of a value-of select to a parameter of another value-of select.
Here is the full code with pseudo statement (myVar)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" />
<xsl:variable name="data" select="document('user.xml')/user/data" />
<xsl:template match="/">
<xsl:for-each select="form/field">
<p class="field">
<xsl:attribute name="style">left:<xsl:value-of
select="left" />;top:<xsl:value-of select="top" />;
</xsl:attribute>
myVar = <xsl:value-of select="text" />
<xsl:value-of select="$data[#key = {#myVar}]/#value" />
</p>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

I am trying to pass the result of a value-of select to a parameter of
another value-of select.
myVar = <xsl:value-of select="text" />
<xsl:value-of select="$data[#key = {#myVar}]/#value" />
The above line is syntactically incorrect -- select is the only attribute that doesn't accept AVT.
In order to achieve the wanted result replace the above with:
<xsl:value-of select="$data[#key = current()/text]/#value" />
Explanation: Proper use of the XSLT current() function.

Related

select xsl:variable values of its child-nodes

I have an .ods file and want to access the values of table-cells in table-rows by the value of the first column for the given row. So their heading in my case.
So the calc table looks like this:
First_Name | Last_Name
Peter | Parker
Emma | Stone
...
Here is my xslt-export-filter file:
SuperBasicExportFilter.xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
exclude-result-prefixes="table text office"
>
<xsl:output
method="xml"
indent="yes"
encoding="UTF-8"
omit-xml-declaration="no"
/>
<xsl:template match="/">
<xsl:variable name="columnHeadings">
<xsl:for-each select="//table:table/table:table-row[not(preceding::table:table-row)]//table:table-cell">
<xsl:element name="heading">
<xsl:attribute name="name" select="text:p" />
<xsl:value-of select="position()" />
</xsl:element>
</xsl:for-each>
</xsl:variable>
<html>
<body>
<h1>Hello</h1>
<xsl:message>columnHeadings: <xsl:value-of select="$columnHeadings" /></xsl:message>
<table>
<xsl:for-each select="//table:table/table:table-row">
<xsl:if test="position() > 1">
<tr>
<td>
First Column Value
<xsl:value-of select="table:table-cell[1]/text:p" />
<!-- <xsl:value-of select="table:table-cell[$columnHeadings/heading[#name='First_Name']]/text:p" /> -->
</td>
<td>
Second Column Value
<xsl:value-of select="table:table-cell[2]/text:p" />
<!-- <xsl:value-of select="table:table-cell[$columnHeadings/heading[#name='Last_Name']]/text:p" /> -->
</td>
</tr>
</xsl:if>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The message shows "columnHeadings: 1234567891011121314" and so on. So it is setting the position values correctly.
I tried getting the values based on the "name" attribute on the "heading" element.
But I can't get the values individually in any way. It seems I can't use the $columnHeadings with any XPath expression. It just returns "Xpath evaluation returned no result".
I tried
wrapping the "heading" elements with a "columnHeadings" element inside the variable and setting the "as" value of the variable to "element()"
using the "node-set" function (after importing the "exslt" ns)
using <xsl:variable name="columnHeadingsNode" select="document('')//xsl:variable[#name = 'columnHeadings']" /> to then get the value
using the xsl:key element like <xsl:key name="columnHeadings" match="//table:table/table:table-row[not(preceding::table:table-row)]//table:table-cell" use="text:p" /> - but this way I can't access it based on the "name"
What other things can I try to access the variable contents with a xpath expression?
Is it even possible to access the values like table:table-cell[$columnHeadings/heading[#name='Last_Name']]?
Answers to comments:
Which XSLT processor are you using?
I'm using whatever libreoffice 7.4.5.1 is using.
Can I change that?
The xsl:vendor is "libxslt" and the version is "1.0" according to the <xsl:value-of select="system-property('xsl:vendor')"/> and xsl:version values.
Do you get an error on <xsl:attribute name="name" select="text:p" />?
I actually do not for some reason. The test runs through without errors. I get a new browser tab with the produced xml output and no errors.
I tried ticking the "The filter needs XSLT 2.0 processor" but then I can't test run the filter anymore and don't get any output.
What's the overall purpose of this exercise?
I want to be able to select the values in the columns by their respective column heading, instead of the index, because I want to make it as portable as possible. At least I think that would help to achieve that goal. I have 184 columns. The column names won't change as likely as the index of the column, I believe.
The intent <xsl:attribute name="name" select="text:p" /> fails to create an attribute with a value in XSLT 1; it should raise an error but it seems your XSLT processor kind of ignores the select.
So try
<xsl:attribute name="name">
<xsl:value-of select="text:p"/>
</xsl:attribute>
instead.
That way, I would think that e.g. <xsl:variable name="columnHeadings-ns" select="exsl:node-set($columnHeadings)" xmlns:exsl="http://exslt.org/common"/> should allow you to use e.g. <xsl:value-of select="table:table-cell[$columnHeadings-ns/heading[#name='First_Name']]/text:p"/>.

XSLT if statement does't work, how do I get it to work?

I'm trying to check if two variables are the same. When the variables are the same, I want to output code.
This is my code right now:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="items">
<xsl:call-template name="item" />
</xsl:template>
<xsl:template name="item">
<xsl:for-each select="item">
<xsl:variable name="currentItemTheme"><xsl:value-of select="title"/></xsl:variable>
<xsl:variable name="theme">{$audienceTheme}</xsl:variable>
<xsl:value-of select="$currentItemTheme"/>    <xsl:value-of select="$theme"/> <br/>
<xsl:if test="$currentItemTheme = $theme">
test <br/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The variable {$audienceTheme} is loaded into the template with Smarty PHP and works correctly. The value for it is Wellness-arrangementen.
This is how the output looks right now:
You can see that 'Wellness-arrangementen' is the {$audienceTheme} variable and that that works.
Now the problem is the if statement under the variables doesn't work. As you can see in the image the second row of text has the same value for both $currentItemTheme and $theme. But the if statement doesn't output 'test <br/>'. Does anyone know why this if statement doesn't work?
In stead of using Smarty PHP to get some dynamic data in the xsl, I would suggest to use the XSLTProcessor->setParam() method like this:
<?php
$xmlDocument = new DOMDocument;
$xslDocument = new DOMDocument;
if ($xmlDocument->load($xmlUri) && $xslDocument->load($xslUri)) {
$xsltProc = new XSLTProcessor();
$xsltProc->setParam('audienceTheme', 'Wellness-arrangementen');
if ($xsltProc->importStyleSheet($xslDocument)) {
$transformed = $xsltProc->transformToXML($xmlDocument);
}
}
and your xslt could the look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="audienceTheme"/>
<xsl:template match="items">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="item">
<xsl:variable name="currentItemTheme" select="title/text()"/>
<xsl:value-of select="$currentItemTheme"/>    <xsl:value-of select="$audienceTheme"/> <br/>
<xsl:if test="normalize-space($currentItemTheme) = normalize-space($audienceTheme)">
<xsl:text>test</xsl:text>
<br/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

how to set Click event on Xslt output

This is my XSLT file:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="//child_4331">
<xsl:value-of select="*"/>
<xsl:value-of select="#value" />
<xsl:attribute name="onclick">
<xsl:call-template name="GetOnClickJavaScript" />
</xsl:attribute>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
How can I set the click event on child_4331 value?
You didn't say, but I'm assuming you want to copy the child_4331 element and add the onclick attribute.
I would get rid of the template matching '/' and create one to match 'child_4331'. Use xsl:copy to create a copy of the element and add the attribute inside it. If the child_4331 element has attributes or child elements you will want to use xsl:apply-templates to pick them up.
Here is a sample snippet. Your solution may vary depending on your desired output. I can't give you more without knowing what your source XML looks like and what you expect to see in the result.
<xsl:template match="child_4331">
<xsl:copy>
<xsl:attribute name="onclick">
<xsl:call-template name="GetOnClickJavaScript" />
</xsl:attribute>
</xsl:copy>
</xsl:template>

Extract Xpaths of all nodes and then their attributes

I am struggling with xslt from the past 2 days, owing to my starter status.My requirement is that given any input XML file ,I want the output to be a list of all the XPaths of all the tags in order in which they appear in the original XML document(parent, then parent,parents Attributes list/child, parent/child/childOFchild and so forth). THe XSLT should not be specific to any single XMl schema. It should work for any XML file, which is a valid one.
Ex:
If the Input XML Is :
<v1:Root>
<v1:UserID>test</v1:UserID>
<v1:Destination>test</v1:Destination>
<v1:entity name="entiTyName">
<v11:attribute name="entiTyName"/>
<v11:attribute name="entiTyName"/>
<v11:attribute name="entiTyName"/>
<v11:filter type="entiTyName">
<v11:condition attribute="entiTyName" operator="eq" value="{FB8D669E-D090-E011-8F43-0050568E222C}"/>
<v11:condition attribute="entiTyName" operator="eq" value="1"/>
</v11:filter>
<v11:filter type="or">
<v11:filter type="or">
<v11:filter type="and">
<v11:filter type="and">
<v11:condition attribute="cir_customerissuecode" operator="not-like" value="03%"/>
</v11:filter>
</v11:filter>
</v11:filter>
</v11:filter>
</v1:entity>
</v1:Root>
I want my output to be :
/v1:Root/v1:UserID
/v1:Root/v1:Destination
/v1:Root/v1:entity/#name
/v1:Root/v1:entity/v11:attribute
/v1:Root/v1:entity/v11:attribute/#name
/v1:Root/v1:entity/v11:attribute[2]
/v1:Root/v1:entity/v11:attribute[2]/#name
/v1:Root/v1:entity/v11:attribute[3]
/v1:Root/v1:entity/v11:attribute[3]/#name
/v1:Root/v1:entity/v11:filter/#type
/v1:Root/v1:entity/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter/v11:condition/#attribute
/v1:Root/v1:entity/v11:filter/v11:condition/#operator
/v1:Root/v1:entity/v11:filter/v11:condition/#value
/v1:Root/v1:entity/v11:filter/v11:condition[2]
/v1:Root/v1:entity/v11:filter/v11:condition[2]/#attribute
/v1:Root/v1:entity/v11:filter/v11:condition[2]/#operator
/v1:Root/v1:entity/v11:filter/v11:condition[2]/#value
/v1:Root/v1:entity/v11:filter[2]/v11:filter/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition/#attribute
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition/#operator
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition/#value
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition/#attribute
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition/#operator
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition/#value
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]/#attribute
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]/#operator
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]/#value
So, it is basically all the XPath of each element ,then the Xpath of the elements Attributes.
I have an XSLT with me, which is like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" />
<xsl:template match="*[not(child::*)]">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="concat('/', name())" />
<xsl:if test="count(preceding-sibling::*[name() = name(current())]) != 0">
<xsl:value-of
select="concat('[', count(preceding-sibling::*[name() = name(current())]) + 1, ']')" />
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="*" />
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="*" />
</xsl:template>
</xsl:stylesheet>
THe output which gets Produced does not cater to complex tags and also the tag's attributes in the resulting Xpath list :(.
Kindly help me in fixing this xslt to produce the output as mentioned above.
THe present output from the above XSLT is like this :
/v1:Root/v1:UserID
/v1:Root/v1:Destination
/v1:Root/v1:entity/v11:attribute
/v1:Root/v1:entity/v11:attribute[2]
/v1:Root/v1:entity/v11:attribute[3]
/v1:Root/v1:entity/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter[2]/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter[2]/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter[2]/v11:condition[3]
I think there's a discrepancy between your sample input and output, in that the output describes a filter element with two conditions that's not in the source XML. At any rate, I believe this works:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" />
<!-- Handle attributes -->
<xsl:template match="#*">
<xsl:apply-templates select="ancestor-or-self::*" mode="buildPath" />
<xsl:value-of select="concat('/#', name())"/>
<xsl:text>
</xsl:text>
</xsl:template>
<!-- Handle non-leaf elements (just pass processing downwards) -->
<xsl:template match="*[#* and *]">
<xsl:apply-templates select="#* | *" />
</xsl:template>
<!-- Handle leaf elements -->
<xsl:template match="*[not(*)]">
<xsl:apply-templates select="ancestor-or-self::*" mode="buildPath" />
<xsl:text>
</xsl:text>
<xsl:apply-templates select="#*" />
</xsl:template>
<!-- Outputs a path segment for the matched element: '/' + name() + [ordinalPredicate > 1] -->
<xsl:template match="*" mode="buildPath">
<xsl:value-of select="concat('/', name())" />
<xsl:variable name="sameNameSiblings" select="preceding-sibling::*[name() = name(current())]" />
<xsl:if test="$sameNameSiblings">
<xsl:value-of select="concat('[', count($sameNameSiblings) + 1, ']')" />
</xsl:if>
</xsl:template>
<!-- Ignore text -->
<xsl:template match="text()" />
</xsl:stylesheet>

How to get the node from a node-set matching an attribute value?

I have a node-set stored in a variable like below
<xsl:variable name="myXML">
<list>
<input name="First" elementName="FirstName" option="one" />
<input name="Second" elementName="SecondName" option="Two" />
<input name="Third" elementName="ThirdName" option="Three" />
<input name="Fourth" elementName="FourthName" option="Four" />
</list>
</xsl:variable>
My code below retrieves the node and its attributes correctly.
But the for-each in the below code repeats even after finding the match, until it reaches the last <input> node.
So if i have a big list with many <input> nodes in my node-set, it may cause performance issue.
I need to re-factor the below code much simpler, may be without for-each.
<xsl:template match="/">
<xsl:variable name="checkName" select="'Third'" />
<xsl:variable name="getNode">
<xsl:for-each select="$myXML/list/input">
<xsl:if test="./#name=$checkName">
<xsl:copy-of select="." />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="element" select="$getNode/input/#elementName" />
<xsl:variable name="option" select="$getNode/input/#option" />
<element><xsl:value-of select="$element" /></element>
<option><xsl:value-of select="$option" /></option>
</xsl:template>
All i wanted is, i have a input variable checkName="Third" and i need the value of the attributes 'elementName' and 'option' in two different variables that matches the value in the name attribute of the <input> node. Please help me with a solution and also i don't want to use exslt or any other extensions.
My code below retrieves the node and
its attributes correctly. But the
for-each in the below code repeats
even after finding the match, until it
reaches the last node. So if i
have a big list with many
nodes in my node-set, it may cause
performance issue. I need to re-factor
the below code much simpler, may be
without for-each.
<xsl:variable name="getNode">
<xsl:for-each select="$myXML/list/input">
<xsl:if test="./#name=$checkName">
<xsl:copy-of select="." />
</xsl:if>
</xsl:for-each>
</xsl:variable>
Use:
<xsl:variable name="getNode" select="$myXml/list/input[#name=$checkName]"/>
Use a predicate filter to compare the #name to the checkName variable.
The stylesheet can be greatly simplified by removing the for-each and extra variables to use simple XPATH statements:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:variable name="myXML">
<list>
<input name="First" elementName="FirstName" option="one" />
<input name="Second" elementName="SecondName" option="Two" />
<input name="Third" elementName="ThirdName" option="Three" />
<input name="Fourth" elementName="FourthName" option="Four" />
</list>
</xsl:variable>
<xsl:template match="/">
<xsl:variable name="checkName" select="'Third'" />
<element><xsl:value-of select="$myXML/list/input[#name=$checkName]/#elementName" /></element>
<option><xsl:value-of select="$myXML/list/input[#name=$checkName]/#option" /></option>
</xsl:template>
</xsl:stylesheet>