I have the following XSLT snippet:
<xsl:for-each
select="distinct-values(/summary/results[#count eq $currentResult]/simulator/host/tps)">
<th>
<small>
TPS Avg
<br></br>
</small>
</th>
<th>
<small>
TPS 95th%tile
<br></br>
</small>
</th>
</xsl:for-each>
Since, I have used distinct-values I would expect this to iterate once through the loop, but it iterates twice. Twice makes no sense even if distinct-values was not working properly, as there are 3 distinct instances of 'tps' in the XML document. Any ideas appreciated...
distinct-values on a node:
distinct-values(/summary/results[#count eq $currentResult]/simulator/host/tps
is different to distinct-values on an atomic value:
distinct-values(/summary/results[#count eq $currentResult]/simulator/host/tps/avg/text()
When you use a node, the children of that node have to be identical in order for it to extract one entry from the XML tree.
Consider the following:
<tps>
<avg>8.99</avg>
<percentile>11.0</percentile>
</tps>
<tps>
<avg>8.99</avg>
<percentile>11.0</percentile>
</tps>
<tps>
<avg>8.99</avg>
<percentile>10.0</percentile>
</tps>
You might think that select-distinct will only return one entry for <tps> here but it considers children in this case. In the case above there are 2 distinct nodes: avg and percentile are identical in two of the nodes. This is why it looped over the for-each twice.
Related
It is possible to remove parent node and previous parent node if the matched node is empty?
Example:
<div>
<div>
<p>Banana
</p>
</div>
<table>
<tbody>Not empty</tbody>
<tr>
<td>A</td>
</tr>
</table>
<div>
<p>Apple
</p>
</div>
<table>
<tbody></tbody>
</table>
</div>
If <table>-><tbody> is empty I would like to remove <table> and previous <div> node.
Example output:
<div>
<div>
<p>Banana
</p>
</div>
<table>
<tbody>Not empty</tbody>
<tr>
<td>A</td>
</tr>
</table>
</div>
There is no operation in XSLT to "remove" a node. A node is removed unless you actively copy it to the output. If the template rule that matches a node is empty (does nothing) then the node will effectively be removed. So you can write
<xsl:template match="div[following-sibling::*[1]
[self::table[not(string(tbody))]]]"/>
Which matches any div followed by a table with an empty tbody, and does nothing.
This assumes that your stylesheet is processing elements using a recursive-descent apply-templates operation in the normal way, and that this is the best-match rule for these nodes.
I read other threads related to my issue but it did not solve the problem.
<h2 class="tabellen_ueberschrift al">Cards</h2>
<div class="fl" style="width:49%;">
<table class="tabelle_grafik lh" cellpadding="2" cellspacing="1">
<tr>
<th class="al" colspan="3">CA Osasuna</th>
</tr>
<td class="s10 al">
Sisi
<br />
26. min. 2. yellow card, Time wasting </td>
</tr>
I want to get all the a tags (there will be several) within the table so my code is this:
header = soup.find('h2', text="Cards")
cards_table = header.find_next_siblings(limit=2)
for row in cards_table.find_all('a'):
print row
This raises me
AttributeError: 'ResultSet' object has no attribute 'find_all'
cards_table is a table and I iterate over it with the for loop so not sure why this is causing the error. Ideas please?
Ok, the code was missing one line:
for line in cards_table:
for row in line.find_all('a'):
print row
cards_table is a list so we had to iterate over it before we could use the find_all method for the table.
I'm writing an xpath and I wrote the xpath which matches some nodes in the document tree
But I'd like to match all the other elements instead, so I'd like to inverse the selection in the context of the whole document
So let's say we have the following xpath
//*[#id='menu']//*
and document
<body>
<div>
<h1>title</h1>
</div>
<div>
<div id="menu">
<UL>
<LI>home</LI>
<LI>about</LI>
</UL>
</div>
<div id="sidebar">
<ul>
<li>one</li>
<li>two</li>
</ul>
</div>
<div>
</body>
so the uppercased nodes are matched, but what I want to achieve is mark all the lowercase nodes.
Thanks for your help
Your original XPath expression
//*[#id='menu']//*
matches all element nodes that are descendants of the <div id="menu"> (but not the div element itself). Depending on exactly what you mean by the "inverse" you could try something like
//*[not(ancestor::*[#id='menu'])]
which matches all element nodes that do not have an ancestor with id="menu", which would include the div with id="menu" but not its children or grandchildren. If you want to exclude the div as well, use ancestor-or-self:: instead of ancestor::
If you have XPath 2.0 then a more general answer to your original question is to look at the except operator - in XPath 2.0 you can say X except Y to select all the nodes matched by expression X but not also by Y
//* except //*[#id='menu']//*
There are also union (nodes matched by either X or Y) and intersect (both X and Y) operators.
I'm using XSLT to pull some data from child nodes in an Umbraco (4.7) structure. That part is working fine, but I need to style each <li> item slightly different, so each one will be: <li id=x> where x is an integer between 1 and 15.
I've found a couple of methods for incrementing a counter value in XSLT, but I can't tell why it's not working as it should. Here's the relevant source:
<ul>
<xsl:for-each select="$currentPage/Solutions/SolutionsItem[#isDoc]">
<xsl:variable name="count">
<xsl:number/>
</xsl:variable>
<li id="$count">
<a><xsl:value-of select="solutionsItemTitle" /></a>
</li>
</xsl:for-each>
</ul>
When I review the HTML's source after running the XSLT, it just shows <li id=$count> rather than an integer. Can anyone suggest where to go from here?
You need an attribute value template <li id="{$count}">...</li> to compute the attribute value from the XPath expression.
I'm delving into XML and XSLT and am trying to generate a basic, tabular web page. The basic layout of the table seems to be ok, but I'm getting a column of " characters before I get my two-column table (which itself is in the second column of the web page). This is shown below:
There are exactly the number of " characters as there are elements of the XML file this is built from. The code that I think is causing the problem is listed below:
<tbody>
<xsl:for-each select="command">
<tr>
<td width="50%">
<xsl:value-of select="description"/>
</td>"
<td width="50%">
<xsl:value-of select="TLC"/>
</td>
</tr>
</xsl:for-each>
</tbody>
Is the character being generated in each xsl:for-each select? In the event that the above snippet of code looks good, I'll include the entirety of the XSLT file below. Feel free to let me know how dumpy my stuff looks, as I'm coming from a firmware and .NET background.
Thanks.
EDIT: Removed the full body of code since the answer, so obvious that I should smack myself, doesn't involve it.
The problem is right here:
</td>"
<td width="50%">
You're inserting a " which is not inside a cell, thus the browser displays it outside...
The code that I think is causing the
problem is listed below:
<tbody>
<xsl:for-each select="command">
<tr>
<td width="50%">
<xsl:value-of select="description"/>
</td>"
<td width="50%">
<xsl:value-of select="TLC"/>
</td>
</tr>
</xsl:for-each>
</tbody>
This is obvious: Just remove the " character in the 6th line of the above code snippet.