XSL Conditional Totalling - xslt

Is it possible to perform conditional totalling in xsl?
I have the following xml sample:
<?xml version="1.0" encoding="utf-8"?>
<export>
<stats set="1">
<columns>
<column id="0">
<sum>100</sum>
</column>
<column id="1">
<sum>102</sum>
</column>
<column id="2">
<sum>12</sum>
</column>
</columns>
</stats>
<stats set="2">
<columns>
<column id="0">
<sum>100</sum>
</column>
<column id="1">
<sum>101</sum>
</column>
<column id="2">
<sum>19</sum>
</column>
</columns>
</stats>
</export>
Is it possible to compute the total of all columns in each stat set where they are not equal to one another? So it would output the following:
Set 1 Set 2 Diff(Set 1 - Set 2)
Total (Diff) 114 120 -6
column 2 102 101 1
column 3 12 19 -7
So in the output column 1 would be omitted as the sum in the two stat sets is the same.
I can get my xsl to output the columns that are different but unsure how to total these up and put in the total row.
Many thanks,
Andez

This transformation (64 lines):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kColByPosAndVal" match="column"
use="concat(count(preceding-sibling::*),
'+',
sum)"/>
<xsl:key name="kIdByVal" match="column/#id"
use="."/>
<xsl:template match="/*">
<xsl:variable name="vsum1" select=
"sum(stats[#set=1]/*/column
[not(key('kColByPosAndVal',
concat(count(preceding-sibling::*),
'+',
sum)
)[2]
)])"/>
<xsl:variable name="vsum2" select=
"sum(stats[#set=2]/*/column
[not(key('kColByPosAndVal',
concat(count(preceding-sibling::*),
'+',
sum)
)[2]
)])"/>
Set 1 Set 2 Diff(Set 1 - Set 2)
Total (Diff) <xsl:text/>
<xsl:value-of select="$vsum1"/>
<xsl:text> </xsl:text>
<xsl:value-of select="$vsum2"/>
<xsl:text> </xsl:text>
<xsl:value-of select="$vsum1 -$vsum2"/>
<xsl:text>
</xsl:text>
<xsl:for-each select=
"*/*/column/#id
[generate-id()
= generate-id(key('kIdByVal',.)[1])
]
[not(key('kColByPosAndVal',
concat(count(../preceding-sibling::*),
'+',
../sum)
)[2]
)]">
<xsl:variable name="vcolSet1" select=
"/*/stats[#set=1]/*/column[#id=current()]/sum"/>
<xsl:variable name="vcolSet2" select=
"/*/stats[#set=2]/*/column[#id=current()]/sum"/>
Column <xsl:value-of select=".+1"/><xsl:text/>
<xsl:text> </xsl:text>
<xsl:value-of select="$vcolSet1"/>
<xsl:text> </xsl:text>
<xsl:value-of select="$vcolSet2"/>
<xsl:text> </xsl:text>
<xsl:value-of select="$vcolSet1 -$vcolSet2"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<export>
<stats set="1">
<columns>
<column id="0">
<sum>100</sum>
</column>
<column id="1">
<sum>102</sum>
</column>
<column id="2">
<sum>12</sum>
</column>
</columns>
</stats>
<stats set="2">
<columns>
<column id="0">
<sum>100</sum>
</column>
<column id="1">
<sum>101</sum>
</column>
<column id="2">
<sum>19</sum>
</column>
</columns>
</stats>
</export>
produces the wanted, correct result:
Set 1 Set 2 Diff(Set 1 - Set 2)
Total (Diff) 114 120 -6
Column 2 102 101 1
Column 3 12 19 -7
Explanation:
The key named kColByPosAndVal is used to select all columns that have a given position (among all column siblings) and a given value for their sum child-element.
The key named kIdByVal is used in Muenchian grouping to find all different values for the id attribute.
The two totals are calculated summing only those columns, whose kColByPosAndVal key selects only one column element (if it selects two column elements, they both are at the same position and have the same sum).
The rest should be easy to understand.

Related

XSLT: Difference/Sum function calculation over an amount field determined by Debit or Credit from another field

Searched but could not find any related posts - hope someone can help.
I am trying to figure out how to get the difference of the sum of all credits and the sum all debits for a given company - this generates only one row in a journal.
The third field establishes whether the amount is a debit or a credit in the example below.
The file contains a number of fields (pipe delimited) but only ones needed here are the following:
* Col1 = Companies
* Col2 = Amount (All positive values)
* Col3 = Amount Type containing Credits or Debits ("C" or "D" values)
File Example:
----------------
A|200.00|D
A|250.00|C
A|100.00|D
B|50.00|D
B|25.00|D
C|20.00|D
C|25.00|C
C|10.00|D
C|5.00|D
The rows for these sums should be: sum(Debits) - sum(Credits)
Company A should = 50.00 (300.00 - 250.00)
Company B should = 75.00 (75.00 - 0.00)
Company C should = 10.00 (35.00 - 25.00)
How can I fix the calculation? --toward bottom of code.
The code I have sums up the total amount without distinguishing between credits and debits, but I need the difference between those two sums.
For company "A", the following code yields 550.00 instead of the desired 50.00 of sum(Debits) - sum(Credits)
<xsl:value-of select="sum(currentgroup()/col2)"/>
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="root">
<!-- TODO: Auto-generated template -->
<root>
<!-- All companies except "A" Company -->
<xsl:for-each-group select="row[col1 !='A']" group-by ="col1">
<journal>
<xsl:for-each select="current-group()">
<row>
<Company>
<xsl:value-of select="col1"/>
</Company>
<Amount>
<xsl:value-of select="col2"/>
</Amount>
<AmountType>
<xsl:value-of select="col3"/>
</AmountType>
</row>
</xsl:for-each>
</journal>
</xsl:for-each-group>
<!-- only for "A" -->
<journal>
<xsl:for-each-group select="row" group-by ="col1">
<xsl:choose>
<!-- The details from input file for Company "A" -->
<xsl:when test="current-group()/col1='A'">
<xsl:for-each select="current-group()">
<row>
<Company>
<xsl:value-of select="col1"/>
</Company>
<Amount>
<xsl:value-of select="col2"/>
</Amount>
<AmountType>
<xsl:value-of select="col3"/>
</AmountType>
</row>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="current-group()[1]">
<row>
<Company>
<xsl:value-of select="'A'"/>
</Company>
<Amount>
<xsl:value-of select="sum(current-group()/col2)"/>
</Amount>
<AmountType>
<xsl:value-of select="col3"/>
</AmountType>
</row>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</journal>
</root>
</xsl:template>
</xsl:stylesheet>
You are currently doing this to get the sum... (or rather, you are doing currentgroup() without the hyphen in your XSLT, which is a typo).
<xsl:value-of select="sum(current-group()/col2)"/>
Assuming col3 holds either C or D, then you can do this
<xsl:value-of select="sum(current-group()[col3 = 'C']/col2) - sum(current-group()[col3 = 'D']/col2)"/>

XML to pipe-delimited - How to copy/duplicate data to different rows

I'm new to XSLT and trying to transform from XML to pipe-delimited format. The problem that I'm having is that in the output, each claim has to be duplicated for each service line.
Expected Output:
EP030315706890704|TESTSUBMITTER|FAMILY HEALTHCARE|1122334455|1|99214|179.00
EP030315706890704|TESTSUBMITTER|FAMILY HEALTHCARE|1122334455|2|2000F|0.00
EP030315706890705|TESTSUBMITTER2|FAMILY HEALTHCARE|1122334455|1|99214|179.00
EP030315706890705|TESTSUBMITTER2|FAMILY HEALTHCARE|1122334455|2|2000F|0.00
Input XML looks as follows:
<payloadContainer>
<afile>
<clm>
<hdr>
<corn>EP030315706890704</corn>
<idSend>112233445</idSend>
<nmSend>TESTSUBMITTER</nmSend>
</hdr>
<provBill>
<name>
<nmOrg>FAMILY HEALTHCARE</nmOrg>
</name>
<id T="XX" P="P">1122334455</id>
</provBill>
<serv S="1">
<numLine>1</numLine>
<prof>
<px L="S">
<cdPx T="HC">99214</cdPx>
</px>
<amtChrg>179.00</amtChrg>
</prof>
</serv>
<serv S="2">
<numLine>2</numLine>
<prof>
<px L="S">
<cdPx T="HC">2000F</cdPx>
</px>
<amtChrg>0.00</amtChrg>
</prof>
</serv>
</clm>
<clm>
<hdr>
<corn>EP030315706890705</corn>
<idSend>112233445</idSend>
<nmSend>TESTSUBMITTER2</nmSend>
</hdr>
<provBill>
<name>
<nmOrg>FAMILY HEALTHCARE</nmOrg>
</name>
<id T="XX" P="P">1122334455</id>
</provBill>
<serv S="1">
<numLine>1</numLine>
<prof>
<px L="S">
<cdPx T="HC">99214</cdPx>
</px>
<amtChrg>179.00</amtChrg>
</prof>
</serv>
<serv S="2">
<numLine>2</numLine>
<prof>
<px L="S">
<cdPx T="HC">2000F</cdPx>
</px>
<amtChrg>0.00</amtChrg>
</prof>
</serv>
</clm>
</afile>
</payloadContainer>
Desired output XML:
<Table>
<row>
.... All the fields represented here.
</row>
</Table>
Possible solution: https://www.dropbox.com/s/wzvtzw7ihtgxx9o/claimtoRedshift.xsl
This scenario creates two row's dynamically. However, I'm still stuck at how to duplicate for each service line.
I don't see the connection of the linked XSLT to the question presented here. AFAICT, the following stylesheet will return the expected pipe-delimited output:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/payloadContainer">
<xsl:for-each select="afile/clm">
<xsl:variable name="common">
<xsl:value-of select="hdr/corn"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="hdr/nmSend"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="provBill/name/nmOrg"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="provBill/id"/>
<xsl:text>|</xsl:text>
</xsl:variable>
<xsl:for-each select="serv">
<xsl:value-of select="$common"/>
<xsl:value-of select="numLine"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="prof/px/cdPx"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="prof/amtChrg"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

XSLT - Why are multiple nodes getting merged in the output ( Group By)

All-
I am trying to understand the root cause for 2 nodes getting merged.
The input XML
<?xml version='1.0' encoding='UTF-8'?>
<Employees>
<Employee>
<Employee_ID>E00001</Employee_ID>
<Legal_Name Descriptor="John Doe" />
<lastName>Doe</lastName>
<firstName>John</firstName>
<P_From_Date>2015-04-01-08:00</P_From_Date>
<P_End_Date>2015-12-31-08:00</P_End_Date>
<Transaction>
<Plan Descriptor="Plan A" />
<effective_date>2015-03-22-08:00</effective_date>
<end_date>2015-10-22</end_date>
<Annual_Cost>6000</Annual_Cost>
</Transaction>
<Transaction>
<Plan Descriptor="Plan A" />
<effective_date>2015-02-03-08:00</effective_date>
<Annual_Cost>4000</Annual_Cost>
</Transaction>
<Transaction>
<Plan Descriptor="Plan A" />
<effective_date>2013-02-03-08:00</effective_date>
<Annual_Cost>3000</Annual_Cost>
</Transaction>
<Transaction>
<Plan Descriptor="Plan B" />
<effective_date>2014-12-03-08:00</effective_date>
<Annual_Cost>12000</Annual_Cost>
</Transaction>
<Transaction>
<Plan Descriptor="Plan B" />
<effective_date>2014-10-03-08:00</effective_date>
<Annual_Cost>1000</Annual_Cost>
</Transaction>
</Employee>
<Employee>
<Employee_ID>E00002</Employee_ID>
<Legal_Name Descriptor="John Doe" />
<lastName>Test</lastName>
<firstName>Jane</firstName>
<P_From_Date>2015-01-01-08:00</P_From_Date>
<Transaction>
<Plan Descriptor="Plan D" />
<effective_date>2015-05-22-08:00</effective_date>
<Annual_Cost>12000</Annual_Cost>
</Transaction>
<Transaction>
<Plan Descriptor="Plan D" />
<effective_date>2014-03-01-08:00</effective_date>
<Annual_Cost>9000</Annual_Cost>
</Transaction>
<Transaction>
<Plan Descriptor="Plan C" />
<effective_date>2014-12-03-08:00</effective_date>
<Annual_Cost>3000</Annual_Cost>
</Transaction>
<Transaction>
<Plan Descriptor="Plan C" />
<effective_date>2013-01-03-08:00</effective_date>
<Annual_Cost>3000</Annual_Cost>
</Transaction>
</Employee>
</Employees>
And the XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<!-- TODO customize transformation rules
syntax recommendation http://www.w3.org/TR/xslt
-->
<xsl:variable name="newline" select="'
'"/>
<xsl:variable name="delimiter">,</xsl:variable>
<xsl:variable name="qualifier">
<xsl:text>"</xsl:text>
</xsl:variable>
<xsl:template match="/Employees">
<EES>
<xsl:apply-templates/>
</EES>
</xsl:template>
<xsl:template match="Employee">
<EE>
<EID>
<xsl:value-of select="Employee_ID" />
</EID>
<FULNAME>
<xsl:value-of select="Legal_Name/#Descriptor"/>
</FULNAME>
<LNAME>
<xsl:value-of select="lastName"/>
</LNAME>
<FNAME>
<xsl:value-of select="firstName"/>
</FNAME>
<xsl:variable name="bework">
<xsl:for-each-group select="Transaction" group-by="Plan/#Descriptor" >
<berow>
<CurrentGroup>
<xsl:value-of select="current-grouping-key()"/>
</CurrentGroup>
<parm_from_date><xsl:value-of select="../P_From_Date" /></parm_from_date>
<xsl:for-each select="current-group()">
<begin-date><xsl:copy-of select="effective_date" /></begin-date>
<include-flag >
<xsl:choose>
<xsl:when test="xs:date( substring(effective_date,1,10)) >= xs:date(substring(../P_From_Date,1,10))">
<xsl:text>Y</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>N</xsl:text>
</xsl:otherwise>
</xsl:choose>
</include-flag>
<adj-begin-date>
<xsl:choose>
<xsl:when test="xs:date(effective_date) >= xs:date(../P_From_Date_1) ">
<xsl:value-of select="effective_date"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="../P_From_Date_1"/>
</xsl:otherwise>
</xsl:choose>
</adj-begin-date>
<end_date>
<xsl:value-of select="current-group()/end_date"/>
</end_date>
<Cost>
<xsl:value-of select="Annual_Cost"/>
</Cost>
</xsl:for-each>
</berow>
</xsl:for-each-group>
</xsl:variable>
<xsl:for-each select="$bework/berow">
<CGR>
<xsl:value-of select="CurrentGroup" />
</CGR>
<PFRMDT>
<xsl:value-of select="parm_from_date" />
</PFRMDT>
<BDT>
<xsl:value-of select="begin-date" />
</BDT>
<FLAG>
<xsl:value-of select="include-flag" />
</FLAG>
<ADJBGNDT>
<xsl:value-of select="$qualifier"/>
</ADJBGNDT>
<EDATE>
<xsl:value-of select="$qualifier"/>
</EDATE>
<CO>
<xsl:value-of select="Cost" />
</CO>
</xsl:for-each>
</EE>
</xsl:template>
</xsl:stylesheet>
The result
<?xml version="1.0" encoding="UTF-8"?>
<EES xmlns:xs="http://www.w3.org/2001/XMLSchema">
<EE>
<EID>E00001</EID>
<FULNAME>John Doe</FULNAME>
<LNAME>Doe</LNAME>
<FNAME>John</FNAME>
<CGR>Plan A</CGR>
<PFRMDT>2015-04-01-08:00</PFRMDT>
<BDT>2015-03-22-08:00 2015-02-03-08:00 2013-02-03-08:00</BDT>
<FLAG>N N N</FLAG>
<ADJBGNDT>"</ADJBGNDT>
<EDATE>"</EDATE>
<CO>6000 4000 3000</CO>
<CGR>Plan B</CGR>
<PFRMDT>2015-04-01-08:00</PFRMDT>
<BDT>2014-12-03-08:00 2014-10-03-08:00</BDT>
<FLAG>N N</FLAG>
<ADJBGNDT>"</ADJBGNDT>
<EDATE>"</EDATE>
<CO>12000 1000</CO>
</EE>
<EE>
<EID>E00002</EID>
<FULNAME>John Doe</FULNAME>
<LNAME>Test</LNAME>
<FNAME>Jane</FNAME>
<CGR>Plan D</CGR>
<PFRMDT>2015-01-01-08:00</PFRMDT>
<BDT>2015-05-22-08:00 2014-03-01-08:00</BDT>
<FLAG>Y N</FLAG>
<ADJBGNDT>"</ADJBGNDT>
<EDATE>"</EDATE>
<CO>12000 9000</CO>
<CGR>Plan C</CGR>
<PFRMDT>2015-01-01-08:00</PFRMDT>
<BDT>2014-12-03-08:00 2013-01-03-08:00</BDT>
<FLAG>N N</FLAG>
<ADJBGNDT>"</ADJBGNDT>
<EDATE>"</EDATE>
<CO>3000 3000</CO>
</EE>
</EES>
If you look at John Doe's BDT node, there are 2 dates. These are 2 nodes for the sample plan are getting added to the same node despite looping through the current group.
WHat is causing this? And what should be done to remedy this? I will have to use variables as there is more manulations I will have to do. But that is for another day.
Thanks for providng me some insight.
Inside of your bework variable you create a berow element for each Transaction group but then you use <xsl:for-each select="current-group()"> to output a begin-date for each Transaction in that group, without structuring or wrapping them further. With your input that means that berow element can contain two or three begin-date elements.
Then you have <xsl:for-each select="$bework/berow"> and inside
<BDT>
<xsl:value-of select="begin-date" />
</BDT>
which will select and output the string value of the two or three begin-date elements.
I am not sure which value you want to output for BDT, you could use e.g. <xsl:value-of select="begin-date[1]"/> or <xsl:value-of select="begin-date[last()]"/> to output only the string value of the first or last element created earlier.
Perhaps you just need to change:
<BDT>
<xsl:value-of select="begin-date" />
</BDT>
to:
<BDT>
<xsl:copy-of select="begin-date" />
</BDT>
In XSLT 2.0, xsl:value-of will generate a single text node, concatenating the values of all matching nodes, separated by a space (or another separator, if specified).
Additional explanation:
WHat is causing this?
The reason why your nodes are getting merged into a single text node is that you are using xsl:value-of when you try to fetch them from the $bework variable in order to write them into the output tree.
Consider the following simplified example:
XML
<root>
<item>
<category>A</category>
<amount>1000</amount>
</item>
<item>
<category>A</category>
<amount>500</amount>
</item>
<item>
<category>A</category>
<amount>250</amount>
</item>
<item>
<category>B</category>
<amount>600</amount>
</item>
<item>
<category>B</category>
<amount>300</amount>
</item>
</root>
XSLT 2.0
<xsl:stylesheet version="2.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:template match="/root">
<xsl:variable name="groups">
<xsl:for-each-group select="item" group-by="category">
<group category="{current-grouping-key()}">
<xsl:for-each select="current-group()">
<amount>
<xsl:value-of select="amount"/>
</amount>
</xsl:for-each>
</group>
</xsl:for-each-group>
</xsl:variable>
<output>
<xsl:for-each select="$groups/group">
<group category="{#category}">
<copy-of-amount>
<xsl:copy-of select="amount"/>
</copy-of-amount>
<for-each-amount>
<xsl:for-each select="amount">
<new-node value="{.}"/>
</xsl:for-each>
</for-each-amount>
<sum-of-amount>
<xsl:value-of select="sum(amount)"/>
</sum-of-amount>
<value-of-amount>
<xsl:value-of select="amount"/>
</value-of-amount>
</group>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<group category="A">
<copy-of-amount>
<amount>1000</amount>
<amount>500</amount>
<amount>250</amount>
</copy-of-amount>
<for-each-amount>
<new-node value="1000"/>
<new-node value="500"/>
<new-node value="250"/>
</for-each-amount>
<sum-of-amount>1750</sum-of-amount>
<value-of-amount>1000 500 250</value-of-amount>
</group>
<group category="B">
<copy-of-amount>
<amount>600</amount>
<amount>300</amount>
</copy-of-amount>
<for-each-amount>
<new-node value="600"/>
<new-node value="300"/>
</for-each-amount>
<sum-of-amount>900</sum-of-amount>
<value-of-amount>600 300</value-of-amount>
</group>
</output>
As you can see, inside the $myVar variable, each group contains 2-3 distinct amount nodes. You can copy them, sum them or create something for each one of them. However, when you do:
<xsl:value-of select="amount"/>
you are addressing all the amount nodes in the current group, and you will get a result that incorporates them all, same as:
<xsl:value-of select="sum(amount)"/>
returns a result based on all the amount nodes being addressed.
And what should be done to remedy this?
We won't know that until you tell us what is the actual result you want to get. In the comments below you said that:
the final result is really the total cost per employee.
If so, the example above shows how to get it.

XSLT Muenchian sorting

I'm trying to get a better understanding of the Muenchian grouping. I'm restricted to XSL 1.0. I was able to do groupings by attributes but I can't seem to get a grouping by element value to work.
My XML looks like this:
<?xml version="1.0"?>
<orders>
<order date="2015-01-01">
<product amount="8">Apple</product>
<product amount="1">Pear</product>
</order>
<order date="2015-01-01">
<product amount="1">Plum</product>
<product amount="5">Pear</product>
</order>
<order id="01" date="2015-01-03">
<product amount="10">Pear</product>
<product amount="4">Plum</product>
</order>
</orders>
What I'm trying to achieve is building a SVG diagram which shows how many of each fruit were ordered. So that one can easily see which is the top selling fruit per example. This would look like this (NOTE the amount-numbers are not resembling the XML above):
diagram: group by product
The code that I came up with so far is the following:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:svg="http://www.w3.org/2000/svg" >
<xsl:variable name="baseline" select="480"/>
<xsl:key name="group-by-product" match="product" use="." />
<xsl:template match="/orders">
<svg:svg >
<svg:g>
<xsl:apply-templates select="order/product[generate-id(.)=generate-id(key('group-by-product',.)[1])]" />
<!-- draw x- axis and y - axis -->
<svg:path style="stroke-width:2; stroke:black" >
<xsl:attribute name="d">
<xsl:text>M 40 100 L 40 </xsl:text>
<xsl:value-of select="480"/>
<xsl:text> L </xsl:text>
<xsl:value-of select="2* count(order) * 40 + 80" />
<xsl:text> </xsl:text>
<xsl:value-of select="$baseline"/>
<xsl:text> L 40 </xsl:text>
<xsl:value-of select="$baseline"/>
<xsl:text> Z</xsl:text>
</xsl:attribute>
</svg:path>
</svg:g>
</svg:svg>
</xsl:template>
<xsl:template match="order">
<xsl:variable name="y" select="sum(key('order-by-product',product)/#amount)"/>
<svg:rect x="{40 * position()+20}" y="{$baseline - $y}" width="30" height="{$y}" style="fill:blue"/>
<svg:text style="writing-mode:tb" x="{41 * position()+20}" y="{$baseline - $y - 10}">
<xsl:value-of select="$y" />
</svg:text>
<svg:text style="writing-mode:tb" x="{41 * position()+15}" y="{$baseline + 20}">
<xsl:value-of select="product" />
</svg:text>
</xsl:template>
</xsl:stylesheet>
I feel like I have some inconsistencies in my code and confused myself with all the different examples I already looked at..
If possible I would like to avoid "for-each" and use "apply-template" instead.
Thank you for your help!
You've got the Muenchian grouping logic right, but the template wrong - your apply-templates selects product elements:
<xsl:apply-templates select="order/product[generate-id(.)=generate-id(key('group-by-product',.)[1])]" />
but your second template matches order elements so it will never fire. You need to change it to something like
<xsl:template match="product">
<xsl:variable name="y" select="sum(key('order-by-product',.)/#amount)"/>
<svg:rect x="{40 * position()+20}" y="{$baseline - $y}" width="30" height="{$y}" style="fill:blue"/>
<svg:text style="writing-mode:tb" x="{41 * position()+20}" y="{$baseline - $y - 10}">
<xsl:value-of select="$y" />
</svg:text>
<svg:text style="writing-mode:tb" x="{41 * position()+15}" y="{$baseline + 20}">
<xsl:value-of select="." />
</svg:text>
</xsl:template>

Two phase transformation using XSLT 2.0

I am trying to take a CSV file as input and transform it into a XML. I'm new to XSLT and I've found a way to convert a CSV into XML (using an example from Andrew Welch) like so:
Input CSV file:
car manufacturer,model,color,price,inventory
subaru,outback,blue,23195,54
subaru,forester,silver,20495,23
And my output XML would be:
<?xml version="1.0" encoding="UTF-8"?>
<rows>
<row>
<column name="car manufacturer">subaru</column>
<column name="model">outback</column>
<column name="color">blue</column>
<column name="price">23195</column>
<column name="inventory">54</column>
</row>
<row>
<column name="car manufacturer">subaru</column>
<column name="model">forester</column>
<column name="color">silver</column>
<column name="price">20495</column>
<column name="inventory">23</column>
</row>
</rows>
My desired output is actually something similar to:
<stock>
<model>
<car>subaru outback</car>
<color>blue</color>
<price>23195</price>
<inventory>54</inventory>
</model>
<model>
<car>subaru forester</car>
<color>silver</color>
<price>20495</price>
<inventory>23</inventory>
</model>
</stock>
What I read is that it would best be done using a two phase transformation. The CSV to XML is done using XSLT 2.0, so I thought the two phase transformation would be done using that as well without using the node-set function.
So the first phase would be to take the original CSV file as input, and then output the intermediate XML shown above. Then take that intermediate XML, and pass it into another transformation to get the desired output.
Anyone can help on how the two phase transformation can be done? I'm having trouble passing the output of phase one as an input of phase 2?
I have something like this so far:
<xsl:import href="csv2xml.xsl"/>
<xsl:output method="xml" indent="yes" />
<xsl:variable name="intermediate">
<xsl:apply-templates select="/" mode="csv2xml"/>
</xsl:variable>
<xsl:template match="rows" name="main">
**[This is what I'm having trouble with]**
</xsl:template>
I don't see any reason why this transformation needs two phases - except perhaps to allow you to reuse existing code for one of the phases.
However, when you do need two phases, the general model is:
<xsl:template match="/">
<xsl:variable name="phase-1-result">
<xsl:apply-templates select="/" mode="phase-1"/>
</xsl:variable>
<xsl:apply-templates select="$phase-1-result" mode="phase-2"/>
</xsl:template>
with the template rules for phase 1 and phase 2 (and their apply-templates calls) all being in mode phase-1 or phase-2 respectively.
This XSLT 2.0 stylesheet:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="vLines"
select="tokenize(unparsed-text('test.txt'),'(
)?
')"/>
<xsl:variable name="vHeaders"
select="tokenize($vLines[1],',')"/>
<stock>
<xsl:for-each select="$vLines[position()!=1]">
<model>
<xsl:variable name="vColumns" select="tokenize(.,',')"/>
<xsl:for-each select="$vColumns">
<xsl:variable name="vPosition" select="position()"/>
<xsl:variable name="vHeader"
select="$vHeaders[$vPosition]"/>
<xsl:choose>
<xsl:when test="$vHeader = 'car manufacturer'">
<column name="car">
<xsl:value-of
select="(.,$vColumns[
index-of($vHeaders,'model')
])"/>
</column>
</xsl:when>
<xsl:when test="$vHeader = 'model'"/>
<xsl:otherwise>
<column name="{$vHeader}">
<xsl:value-of select="."/>
</column>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</model>
</xsl:for-each>
</stock>
</xsl:template>
</xsl:stylesheet>
Output:
<stock>
<model>
<column name="car">subaru outback</column>
<column name="color">blue</column>
<column name="price">23195</column>
<column name="inventory">54</column>
</model>
<model>
<column name="car">subaru forester</column>
<column name="color">silver</column>
<column name="price">20495</column>
<column name="inventory">23</column>
</model>
</stock>
Note: In XSLT 3.0 you will be able to apply templates to items in general.
EDIT: Correct names.
You can find here an example of how to do this with XSLT 3.0 :
http://www.stylusstudio.com/tutorials/intro-xslt-3.html
And see under "Text Manipulations".