Total of true and false in current time - xpath

I have got this XML document. Is there way how to sort the nodes by ascending time and by brand and then count how many cars (samples), true and false was in this time from start time (always the lowest time in the group)?
<?xml version="1.0" encoding="UTF-8"?>
<trade>
<car time="1950" brand="audi" trend="true">
</car>
<car time="1200" brand="renault" trend="true">
</car>
<car time="1000" brand="audi" trend="true">
</car>
<car time="2800" brand="renault" trend="true">
</car>
<car time="2000" brand="audi" trend="true">
</car>
<car time="1500" brand="renault" trend="true">
</car>
<car time="1900" brand="audi" trend="false">
</car>
<car time="2300" brand="audi" trend="false">
</car>
<car time="2100" brand="renault" trend="false">
</car>
</trade>
Wanted result in HTML

Consider to include what you have got next time in the question text. If you already know how to group and how to sort you can then easily process the sorted sequence and take the subsequence until each item and check the count of the items with a certain trend as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="trade">
<xsl:for-each-group select="car" group-by="#brand">
<h2>{current-grouping-key()}</h2>
<table>
<thead>
<tr>
<th>Total of samples</th>
<th>Time</th>
<th>Total of TRUE</th>
<th>Total of FALSE</th>
</tr>
</thead>
<xsl:variable name="sorted-cars" as="element(car)*">
<xsl:perform-sort select="current-group()">
<xsl:sort select="xs:integer(#time)"/>
</xsl:perform-sort>
</xsl:variable>
<tbody>
<xsl:for-each select="$sorted-cars">
<tr>
<td>{position()}</td>
<td>{#time}</td>
<xsl:variable name="car-group" select="subsequence($sorted-cars, 1, position())"/>
<td>{count($car-group[#trend = 'true'])}</td>
<td>{count($car-group[#trend = 'false'])}</td>
</tr>
</xsl:for-each>
</tbody>
</table>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/nc4NzQ1

Related

Is there a way to conditionally sort in xslt?

I need to sort some content but only when an attribute is equal to CAT. I think I should be able to pass a property from my ant build file to the use-when attribute but it is not working. Any help would be appreciated
Here is the xslt that I have:
<xsl:for-each select="document(#fileRef)/foo/bar">
<xsl:sort select="translate(child::Title/text(), '>', '')" order="ascending" use-when="system-property('customerCode')='CAT'"
collation="http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive"/>
<!-- do some stuff here -->
</xsl:for-each>
Using oXygen I got the following to work in an Ant file:
<xslt in="sample1.xml" out="sample1-transformed.xml" force="true" style="sheet1.xsl">
<factory name="net.sf.saxon.TransformerFactoryImpl"/>
<sysproperty key="cat" value="bar"/>
</xslt>
XML sample1.xml is e.g.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>c</item>
<item>a</item>
</root>
XSLT is
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:comment select="system-property('cat')"/>
<xsl:next-match/>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="item">
<xsl:sort select="." use-when="system-property('cat') = 'foo'"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
and then the output items are sorted only if the Ant sets e.g. <sysproperty key="cat" value="foo"/>.

Sorting using xsl:sort

I have XML file (some service documentation) looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<apis>
<api min="" max="">
<resource name="C">
<description>C from beggining to the end</description>
</resource>
</api>
<api min="2.2" max="">
<resource name="B">
<description>B from 2.2 to the end</description>
</resource>
<resource name="A">
<description>A from 2.2 to the end</description>
</resource>
</api>
</apis>
And XSL file to transform XML into html:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="api-version" select="2.2"></xsl:param>
<xsl:template match="apis">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<table border="1">
<th colspan="2">
<xsl:text>Doc for API: </xsl:text>
<xsl:value-of select="$api-version"/>
</th>
<xsl:call-template name="handleApis"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template name="handleApis">
<xsl:for-each select="api[(#min='' or #min<=$api-version) and (#max='' or #max>$api-version)]">
<xsl:for-each select="resource">
<xsl:call-template name="handleResource"/>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template name="handleResource">
<tr>
<td>
<xsl:value-of select="#name"/>
</td>
<td>
<xsl:value-of select="description"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
And now I want to sort results by resource atribute name.
I have tried to put
<xsl:sort select="#name" order="ascending"/>
inside for-each for resource nodes but like You know it won't sort resources between different api nodes and results is
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<table border="1">
<th colspan="2">Doc for API: 2.2</th>
<tr>
<td>C</td>
<td>C from beggining to the end</td>
</tr>
<tr>
<td>A</td>
<td>A from 2.2 to the end</td>
</tr>
<tr>
<td>B</td>
<td>B from 2.2 to the end</td>
</tr>
</table>
</body>
</html>
Adding
<xsl:sort select="resource/#name" order="ascending"/>
inside for-each for api nodes also can't work because sort need only one item in select atrribute.
There is some way do sort this to get order A, B, C in output?
How about simply:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="api-version" select="2.2"/>
<xsl:template match="/apis">
<html>
<head/>
<body>
<table border="1">
<th colspan="2">
<xsl:text>Doc for API: </xsl:text>
<xsl:value-of select="$api-version"/>
</th>
<xsl:apply-templates select="api[(#min='' or #min <= $api-version) and (#max='' or #max > $api-version)]/resource">
<xsl:sort select="#name"/>
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="resource">
<tr>
<td>
<xsl:value-of select="#name"/>
</td>
<td>
<xsl:value-of select="description"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>

How to access variable as parameter from another template XSLT

Hello I have one problem, I have 2 variables(min_value and max_value) they are in different templates MIN and MAX, in MAX I want to calculate the difference between max_value and min_value but the result is NAN since min_value values are not transferred from one template to another.
<xsl:template match="MAX">
<xsl:param name= "min_value"/>
<xsl:variable name="max_value" select= "SHARE_RATE"/>
<span class="max_rate">
MAX: <xsl:apply-templates select="SHARE_RATE"/>
</span>
<br/>
<span class= "diff">
Diff: (<xsl:value-of select="$max_value - $min_value"/>)
</span>
</xsl:template>
<xsl:template match="MIN">
<xsl:variable name="min_value" select="SHARE_RATE"/>
<span class="SHARE_RATE">
MIN: <xsl:apply-templates select="SHARE_RATE"/>
</span>
<xsl:apply-templates select="MAX">
<xsl:with-param name="min_value" select= "$min_value"/>
</xsl:apply-templates>
</xsl:template>
UPDATE
<page shareid="%" min_rate="%" max_rate="%" skip="0">
<TRANSACTIONS>
<MIN num="1">
<SHAREID>0</SHAREID>
<SHARE_RATE>1200</SHARE_RATE>
</MIN>
<MIN num="2">
<SHAREID>1</SHAREID>
<SHARE_RATE>4200</SHARE_RATE>
</MIN>
<MIN num="3">
<SHAREID>2</SHAREID>
<SHARE_RATE>1600</SHARE_RATE>
</MIN>
<MIN num="4">
<SHAREID>3</SHAREID>
<SHARE_RATE>6100</SHARE_RATE>
</MIN>
<MIN num="5">
<SHAREID>4</SHAREID>
<SHARE_RATE>550</SHARE_RATE>
</MIN>
<MIN num="6">
<SHAREID>5</SHAREID>
<SHARE_RATE>420</SHARE_RATE>
</MIN>
<MIN num="7">
<SHAREID>6</SHAREID>
<SHARE_RATE>2000</SHARE_RATE>
</MIN>
</TRANSACTIONS>
<TRANSACTIONS>
<MAX num="1">
<SHAREID>0</SHAREID>
<SHARE_RATE>2100</SHARE_RATE>
</MAX>
<MAX num="2">
<SHAREID>1</SHAREID>
<SHARE_RATE>5200</SHARE_RATE>
</MAX>
<MAX num="3">
<SHAREID>2</SHAREID>
<SHARE_RATE>2000</SHARE_RATE>
</MAX>
<MAX num="4">
<SHAREID>3</SHAREID>
<SHARE_RATE>7000</SHARE_RATE>
</MAX>
<MAX num="5">
<SHAREID>4</SHAREID>
<SHARE_RATE>1000</SHARE_RATE>
</MAX>
<MAX num="6">
<SHAREID>5</SHAREID>
<SHARE_RATE>2520</SHARE_RATE>
</MAX>
<MAX num="7">
<SHAREID>5</SHAREID>
<SHARE_RATE>2520</SHARE_RATE>
</MAX>
</TRANSACTIONS>
</page>
UPDATE 2
<?xml version="1.0" encoding="ISO-8859-2"?>
<?xml-stylesheet type="text/xsl" href="share.xsl"?>
<page connection="labor"
xmlns:xsql="urn:oracle-xsql"
shareid="%"
min_rate= "%"
max_rate= "%"
skip="0">
<xsql:query rowset-element="TRANSACTIONS"
row-element="MIN"
skip-rows="{#skip}"
max-rows="{#max-rows}"
bind-params="shareid min_rate ">
SELECT
f77inq.shares.shareid, A.share_rate
FROM
f77inq.trans A
INNER JOIN f77inq.shares ON A.shareid = f77inq.shares.shareid
WHERE A.shareid LIKE ?
AND A.share_rate=
(
SELECT
MIN(share_rate)
FROM
f77inq.trans B
WHERE
B.shareid = A.shareid
)
AND A.share_rate LIKE?
</xsql:query>
<xsql:query rowset-element="TRANSACTIONS"
row-element="MAX"
skip-rows="{#skip}"
max-rows="{#max-rows}"
bind-params="shareid">
SELECT
f77inq.shares.shareid, A.share_rate
FROM
f77inq.trans A
INNER JOIN f77inq.shares ON A.shareid = f77inq.shares.shareid
WHERE A.shareid LIKE ?
AND A.share_rate=
(
SELECT
MAX(share_rate)
FROM
f77inq.trans B
WHERE
B.shareid = A.shareid
)
</xsql:query>
</page>
I would suggest you use a key to link the MIN and MAX values, based on a common SHAREID. Here's an example that uses XML output for better clarity:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="max" match="MAX" use="SHAREID" />
<xsl:template match="/page">
<root>
<xsl:apply-templates select="TRANSACTIONS/MIN"/>
</root>
</xsl:template>
<xsl:template match="MIN">
<xsl:variable name="id" select="SHAREID" />
<xsl:variable name="min" select="SHARE_RATE" />
<xsl:variable name="max" select="key('max', $id)/SHARE_RATE" />
<share id="{$id}">
<min><xsl:value-of select="$min"/></min>
<max><xsl:value-of select="$max"/></max>
<diff><xsl:value-of select="$max - $min"/></diff>
</share>
</xsl:template>
</xsl:stylesheet>
Applied to your example input, the result is:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<share id="0">
<min>1200</min>
<max>2100</max>
<diff>900</diff>
</share>
<share id="1">
<min>4200</min>
<max>5200</max>
<diff>1000</diff>
</share>
<share id="2">
<min>1600</min>
<max>2000</max>
<diff>400</diff>
</share>
<share id="3">
<min>6100</min>
<max>7000</max>
<diff>900</diff>
</share>
<share id="4">
<min>550</min>
<max>1000</max>
<diff>450</diff>
</share>
<share id="5">
<min>420</min>
<max>2520</max>
<diff>2100</diff>
</share>
<share id="6">
<min>2000</min>
<max>2520</max>
<diff>520</diff>
</share>
</root>
ok, what I understand is, that you want to match MIN/MAX values with the same SHAREID. In this case the following xslt might do what you need:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="MAX">
<xsl:variable name="id" select="SHAREID"/>
<!-- or '0' handles the case where no coresponding MIN/SHARE_RATE is found -->
<xsl:variable name="min_value" select="//MIN[SHAREID=$id]/SHARE_RATE or '0'"/>
<xsl:variable name="max_value" select= "SHARE_RATE"/>
<span class="max_rate">
MAX: <xsl:value-of select="$max_value"/>
</span>
<br/>
<span class= "diff">
Diff: (<xsl:value-of select="$max_value - $min_value"/>)
</span>
</xsl:template>
<!-- MAX does it all, so ignore output of MIN -->
<xsl:template match="MIN"/>
<xsl:template match="*">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

Sorting a table in XSLT 2.0

I have an index file, containing a list of xml files to process:
<?xml version="1.0" encoding="UTF-8"?>
<list>
<part>
<numberGroup format="1" start="1">
<entry>
<file>04.xml</file>
<title>first file</title>
<css>stylesheet.css</css>
</entry>
<entry>
<file>05.xml</file>
<title>second file</title>
<css>stylesheet.css</css>
</entry>
<entry>
<file>06.xml</file>
<title>third file</title>
<css>stylesheet.css</css>
</entry>
.... more files
</numberGroup>
.... more NumberGroups
</part>
....more parts
</list>
every file 01.xml etc has one HTML-style table, like this:
<table class='wl'>
<tr class='header'>
<td><p>English</p></td>
<td><p>French</p></td>
</tr>
<tr>
<td><p>belly</p></td>
<td><p>ventre</p></td>
</tr>
<tr>
<td><p>leg</p></td>
<td><p>jambe</p>/td>
</tr>
... etc
</table>
I want to merge all the tables (3 in this example) into one, so I get a compete vocabulary.
So far I have this stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:result-document href="wl.xml">
<xsl:text disable-output-escaping="yes">
<!DOCTYPE html>
</xsl:text>
<html xmlns:epub="http://www.idpf.org/2007/ops" lang="nl" xml:lang="nl">
<head>
<title>
<xsl:value-of select="./title"/>
</title>
<xsl:apply-templates select="css"/>
</head>
<body>
<table>
<xsl:apply-templates select="//numberGroup[1]/entry">
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:result-document>
</xsl:template>
<xsl:template match="entry">
<xsl:apply-templates select="document(file)//table[#class='wl']/tr[not(contains(#class, 'header'))]"/>
</xsl:template>
<xsl:template match="tr">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
which merges as desired, except for the sorting.
How can I sort the generated table alphabetically, on the first column?
Replace
<xsl:apply-templates select="//numberGroup[1]/entry">
</xsl:apply-templates>
with
<xsl:apply-templates select="document(//numberGroup[1]/entry/file)//table[#class='wl']/tr[not(contains(#class, 'header'))]">
<xsl:sort select="td[1]" data-type="text"/>
</xsl:apply-templates>
then you can drop the template for entry elements.

XPath query for empty namespace xmlns=""

What XPath do I use to query the info node in the xml below? I've tried different expressions in XMLSpy but nothing works.
<root xmlns="tempuri.org" xmlns:p="http://nonamespace.org/std/Name/2006-10-18/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item xmlns="">
<info>blah blah</info>
<date>2009-07-27 00:00:00</date>
</item>
you can do it like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="tempuri.org">
<xsl:template match="/">
<xsl:value-of select="a:root/item/info"/>
</xsl:template>

Resources