I have an xml on which I am applying xsl to transform into pipe delimited file. I am able to get the output but I also need a record count at the end of file. Please help.
XSL:
<?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"
xmlns:wd="urn:com.workday/bsvc"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="text" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="delimeter" select="'|'"/>
<xsl:variable name="lineFeed" select="'
'"/>
<xsl:text>Headers</xsl:text>
<xsl:value-of select="$lineFeed"/>
<!--<xsl:for-each select="wd:Report_Data/wd:Report_Entry">-->
<xsl:for-each-group select="wd:Report_Data/wd:Report_Entry" group-by="wd:Worker_group/wd:Employee_ID">
<!-- HDRCOM - Print Company Details -->
<text>HDRCOM</text>
<xsl:value-of select="$delimeter"/>
<xsl:value-of select="wd:Worker_group/wd:Company_Name"/>
<xsl:value-of select="$delimeter"/>
<xsl:value-of select="wd:Worker_group/wd:Company_Address"/>
<xsl:value-of select="$lineFeed"/>
<!-- HDRBIO (Employee Information) -->
<text>HDRBIO</text>
<xsl:value-of select="$delimeter"/>
<xsl:value-of select="wd:Worker_group/wd:Full_Legal_Name"/>
<xsl:value-of select="$delimeter"/>
<xsl:value-of select="wd:Worker_group/wd:Employee_ID"/>
<xsl:value-of select="$delimeter"/>
<!-- TAXES (Repeat the seqment for each type of Tax) -->
<xsl:for-each select="current-group()">
<xsl:choose>
<xsl:when test="wd:Pay_Component_Category = 'Employee Paid Taxes'">
<text>TAXES</text>
<xsl:value-of select="$delimeter"/>
<xsl:value-of select="wd:Pay_Component"/>
<xsl:value-of select="$delimeter"/>
<xsl:value-of select="wd:Amount"/>
<xsl:value-of select="$delimeter"/>
<xsl:value-of select="wd:YTD_Amount"/>
<xsl:value-of select="$lineFeed"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
<!--</xsl:for-each>-->
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
My output looks like:
HDRCOM|ABC|XYZ
HDRBIO|Andrew|12345
TAXES|Fed Withholdng A| 0.00| 0.00
TAXES|Fed Withholdng B| 1.95| 4.40
TAXES|Fed Withholdng C| 8.37| 18.83
HDRCOM|ABC|XYZ
HDRBIO|John|16543
TAXES|Fed Withholdng A| 1.00| 1.00
TAXES|Fed Withholdng B| 2.65| 4.40
TLR| 09 -----> I need this. TOTAL NUMBER OF LINES PRINTED.
Input:
<?xml version='1.0' encoding='UTF-8'?>
<wd:Report_Data xmlns:wd="urn:com.workday/bsvc">
<wd:Report_Entry>
<wd:Worker_group>
<wd:Company_Name>ABC</wd:Company_Name>
<wd:Company_Address>XYZ</wd:Company_Address>
<wd:Full_Legal_Name>Andrew</wd:Full_Legal_Name>
<wd:Employee_ID>12345</wd:Employee_ID>
</wd:Worker_group>
<wd:Pay_Component>Fed Withholdng A</wd:Pay_Component>
<wd:Amount>0</wd:Amount>
<wd:YTD_Amount>0</wd:YTD_Amount>
<wd:Pay_Component_Category>Employee Paid Taxes</wd:Pay_Component_Category>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:Worker_group>
<wd:Company_Name>ABC</wd:Company_Name>
<wd:Company_Address>XYZ</wd:Company_Address>
<wd:Full_Legal_Name>Andrew</wd:Full_Legal_Name>
<wd:Employee_ID>12345</wd:Employee_ID>
</wd:Worker_group>
<wd:Pay_Component>Fed Withholdng B</wd:Pay_Component>
<wd:Amount>1.95</wd:Amount>
<wd:YTD_Amount>4.40</wd:YTD_Amount>
<wd:Pay_Component_Category>Employee Paid Taxes</wd:Pay_Component_Category>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:Worker_group>
<wd:Company_Name>ABC</wd:Company_Name>
<wd:Company_Address>XYZ</wd:Company_Address>
<wd:Full_Legal_Name>Andrew</wd:Full_Legal_Name>
<wd:Employee_ID>12345</wd:Employee_ID>
</wd:Worker_group>
<wd:Pay_Component>Fed Withholdng C</wd:Pay_Component>
<wd:Amount>8.37</wd:Amount>
<wd:YTD_Amount>18.83</wd:YTD_Amount>
<wd:Pay_Component_Category>Employee Paid Taxes</wd:Pay_Component_Category>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:Worker_group>
<wd:Company_Name>ABC</wd:Company_Name>
<wd:Company_Address>XYZ</wd:Company_Address>
<wd:Full_Legal_Name>John</wd:Full_Legal_Name>
<wd:Employee_ID>16543</wd:Employee_ID>
</wd:Worker_group>
<wd:Pay_Component>Fed Withholdng A</wd:Pay_Component>
<wd:Amount>1</wd:Amount>
<wd:YTD_Amount>1</wd:YTD_Amount>
<wd:Pay_Component_Category>Employee Paid Taxes</wd:Pay_Component_Category>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:Worker_group>
<wd:Company_Name>ABC</wd:Company_Name>
<wd:Company_Address>XYZ</wd:Company_Address>
<wd:Full_Legal_Name>John</wd:Full_Legal_Name>
<wd:Employee_ID>16543</wd:Employee_ID>
</wd:Worker_group>
<wd:Pay_Component>Fed Withholdng B</wd:Pay_Component>
<wd:Amount>2.65</wd:Amount>
<wd:YTD_Amount>4.40</wd:YTD_Amount>
<wd:Pay_Component_Category>Employee Paid Taxes</wd:Pay_Component_Category>
</wd:Report_Entry>
</wd:Report_Data>
A simple way would be to capture the textual record which you're generating as a string variable and then count the number of occurrences of line feed characters it contains.
<xsl:variable name="record">
<!-- insert your record-generation code here -->
</xsl:variable>
<!-- output the body of the record -->
<xsl:sequence select="$record"/>
<!-- compute the line count -->
<xsl:variable name="lines" select="count(tokenize($record, '\n')) + 1"/>
<!-- finally output the $lines information -->
You can use the format-number() function to format the line count with a leading zero if that's what you need. https://www.w3.org/TR/xslt20/#function-format-number
I'd suggest you try it this way:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="urn:com.workday/bsvc">
<xsl:output method="text"/>
<xsl:template match="/Report_Data">
<xsl:variable name="items" select="Report_Entry[Pay_Component_Category = 'Employee Paid Taxes']" />
<xsl:for-each-group select="$items" group-by="Worker_group/Employee_ID">
<!-- HDRCOM - Print Company Details -->
<xsl:value-of select="'HDRCOM', Worker_group/Company_Name, Worker_group/Company_Address" separator="|"/>
<xsl:text>
</xsl:text>
<!-- HDRBIO (Employee Information) -->
<xsl:value-of select="'HDRBIO', Worker_group/Full_Legal_Name, Worker_group/Employee_ID" separator="|"/>
<xsl:text>
</xsl:text>
<!-- TAXES (Repeat the seqment for each type of Tax) -->
<xsl:for-each select="current-group()">
<xsl:value-of select="'TAXES', Pay_Component, Amount, YTD_Amount" separator="|"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each-group>
<!-- count lines -->
<xsl:value-of select="'TLR', count($items) + 2*count(distinct-values($items/Worker_group/Employee_ID))" separator="|"/>
</xsl:template>
</xsl:stylesheet>
Note that there is a difference between my logic and yours: my method will not create headers for an empty group.
Related
I am try to remove duplicate entry after entity § and if contains the , in entry and after tokenize the start-with the ( round bracket then entry e.g (17200(b)(2), (4)–(6)) s/b e.g (<p>17200(b)(2)</p><p>17200(b)(4)–(6)</p>).
Input XML
<root>
<p>CC §1(a), (b), (c)</p>
<p>Civil Code §1(a), (b)</p>
<p>CC §§2(a)</p>
<p>Civil Code §3(a)</p>
<p>CC §1(c)</p>
<p>Civil Code §1(a), (b), (c)</p>
<p>Civil Code §17200(b)(2), (4)–(6), (8), (12), (16), (20), and (21)</p>
</root>
Expected Output
<root>
<sec specific-use="CC">
<title content-type="Sta_Head3">CIVIL CODE</title>
<p>1(a)</p>
<p>1(b)</p>
<p>1(c)</p>
<p>2(a)</p>
<p>3(a)</p>
<p>17200(b)(2)</p>
<p>17200(b)(4)–(6)</p>
<p>17200(b)(8)</p>
<p>17200(b)(12)</p>
<p>17200(b)(16)</p>
<p>17200(b)(20)</p>
<p>17200(b)(21)</p>
</sec>
</root>
XSLT Code
<xsl:template match="root">
<xsl:copy>
<xsl:for-each-group select="p[(starts-with(., 'CC ') or starts-with(., 'Civil Code'))]" group-by="replace(substring-before(., ' §'), 'Civil Code', 'CC')">
<xsl:text>
</xsl:text>
<sec specific-use="{current-grouping-key()}">
<xsl:text>
</xsl:text>
<title content-type="Sta_Head3">CIVIL CODE</title>
<xsl:for-each-group select="current-group()" group-by="replace(substring-after(., '§'), '§', '')">
<xsl:sort select="replace(current-grouping-key(), '[^0-9.].*$', '')" data-type="number" order="ascending"/>
<xsl:for-each
select="distinct-values(
current-grouping-key() !
(let $tokens := tokenize(current-grouping-key(), ', and |, | and ')
return (head($tokens), tail($tokens) ! (substring-before(head($tokens), '(') || .)))
)" expand-text="yes">
<p>{.}</p>
</xsl:for-each>
</xsl:for-each-group>
</sec>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
You could do it like this, in a two-step approach where you first compute the list of existing elements and then use a for-each-group to remove duplicates.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="listP">
<xsl:apply-templates select="root/p"/>
</xsl:variable>
<xsl:for-each-group select="$listP" group-by="p">
<p><xsl:value-of select="current-grouping-key()"/></p>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="p">
<xsl:variable name="input" select="replace(substring-after(.,'§'),'§','')"/>
<xsl:variable name="chapter" select="substring-before($input,'(')"/>
<xsl:for-each select="tokenize(substring-after($input, $chapter),',')">
<p><xsl:value-of select="concat($chapter,replace(replace(.,' ',''),'and',''))"/></p>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
See it working here : https://xsltfiddle.liberty-development.net/gVrvcxQ
I have a payroll XML file that contains a child element(pay_component_code) for every line item row. I'm provided with a mapping document that contains translation between current codes and future codes, either 1:1 or M:1. The requirement is to convert the xml to pipe delimited text file and also aggregate the amount based on a grouping of person number, effective date and pay component code (future state). The xslt I have seems to work fine for a sample population but however, when I ran the program for another group, it errors with following error:
A sequence of more than one item is not allowed as the first argument of concat() (<payCode/>, <payCode/>) Line: 1786 Column: 11
I see it happens when the node in context has more than one child element. Can someone explain what the issue might be? Oxygen editor has not been very helpful as it highlights the entire xml, which is too long to analyse the root issue.
Sample xml:
<?xml version='1.0' encoding='UTF-8'?>
<wd:Report_Data xmlns:wd="urn:com.workday.report/ERP_PAY_CR_PAYROLL_BALANCES_LINE_AMT">
<wd:Report_Entry>
<wd:Country_Code>US</wd:Country_Code>
<wd:Entity_Name>BalanceInitialization</wd:Entity_Name>
<wd:Legislative_Data_Group_Name>US Legislative Data Group</wd:Legislative_Data_Group_Name>
<wd:Legal_Entity_Name wd:Descriptor="SRMH">
<wd:ID wd:type="WID">66b06c2b4ec001df7d2d7875ea020e52</wd:ID>
<wd:ID wd:type="Organization_Reference_ID">G123</wd:ID>
<wd:ID wd:type="Company_Reference_ID">G123</wd:ID>
</wd:Legal_Entity_Name>
<wd:Person_Number>2110013</wd:Person_Number>
<wd:Effective_Date>2020-01-01</wd:Effective_Date>
<wd:Period>2020-Q1</wd:Period>
<wd:Dimension>Assignment Tax Unit Quarter to Date</wd:Dimension>
<wd:Pay_Component_Name>102 Regular - Hourly</wd:Pay_Component_Name>
<wd:Pay_Component_Code>102</wd:Pay_Component_Code>
<wd:Number>3143.28</wd:Number>
<wd:Payroll wd:Descriptor="PSJH Biweekly B">
<wd:ID wd:type="WID">2d405a8ea1ea01265d95c04def2d96c7</wd:ID>
<wd:ID wd:type="Evaluate_Expression_Band_Related_Content_Reference_ID"
>EVALUATE_EXPRESSION_BAND_CALCULATED_FIELD_RELATED_CONTENT-6-2d405a8ea1ea01265d95c04def2d96c7</wd:ID>
</wd:Payroll>
<wd:Tax_Reporting_Unit>SJHNC, LLC</wd:Tax_Reporting_Unit>
<wd:Company_ID>G123</wd:Company_ID>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:Country_Code>US</wd:Country_Code>
<wd:Entity_Name>BalanceInitialization</wd:Entity_Name>
<wd:Legislative_Data_Group_Name>US Legislative Data Group</wd:Legislative_Data_Group_Name>
<wd:Legal_Entity_Name wd:Descriptor="SRMH">
<wd:ID wd:type="WID">66b06c2b4ec001df7d2d7875ea020e52</wd:ID>
<wd:ID wd:type="Organization_Reference_ID">G123</wd:ID>
<wd:ID wd:type="Company_Reference_ID">G123</wd:ID>
</wd:Legal_Entity_Name>
<wd:Person_Number>2110013</wd:Person_Number>
<wd:Effective_Date>2020-01-01</wd:Effective_Date>
<wd:Period>2020-Q1</wd:Period>
<wd:Dimension>Assignment Tax Unit Quarter to Date</wd:Dimension>
<wd:Pay_Component_Name>123 Overtime - OT Prem</wd:Pay_Component_Name>
<wd:Pay_Component_Code>123</wd:Pay_Component_Code>
<wd:Number>185.31</wd:Number>
<wd:Payroll wd:Descriptor="PSJH Biweekly B">
<wd:ID wd:type="WID">2d405a8ea1ea01265d95c04def2d96c7</wd:ID>
<wd:ID wd:type="Evaluate_Expression_Band_Related_Content_Reference_ID"
>EVALUATE_EXPRESSION_BAND_CALCULATED_FIELD_RELATED_CONTENT-6-2d405a8ea1ea01265d95c04def2d96c7</wd:ID>
</wd:Payroll>
<wd:Tax_Reporting_Unit>SJHNC, LLC</wd:Tax_Reporting_Unit>
<wd:Company_ID>G123</wd:Company_ID>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:Country_Code>US</wd:Country_Code>
<wd:Entity_Name>BalanceInitialization</wd:Entity_Name>
<wd:Legislative_Data_Group_Name>US Legislative Data Group</wd:Legislative_Data_Group_Name>
<wd:Legal_Entity_Name wd:Descriptor="SRMH">
<wd:ID wd:type="WID">66b06c2b4ec001df7d2d7875ea020e52</wd:ID>
<wd:ID wd:type="Organization_Reference_ID">G123</wd:ID>
<wd:ID wd:type="Company_Reference_ID">G123</wd:ID>
</wd:Legal_Entity_Name>
<wd:Person_Number>2110013</wd:Person_Number>
<wd:Effective_Date>2020-01-01</wd:Effective_Date>
<wd:Period>2020-Q1</wd:Period>
<wd:Dimension>Assignment Tax Unit Quarter to Date</wd:Dimension>
<wd:Pay_Component_Name>126 Overtime - OT Base</wd:Pay_Component_Name>
<wd:Pay_Component_Code>126</wd:Pay_Component_Code>
<wd:Number>370.61</wd:Number>
<wd:Payroll wd:Descriptor="PSJH Biweekly B">
<wd:ID wd:type="WID">2d405a8ea1ea01265d95c04def2d96c7</wd:ID>
<wd:ID wd:type="Evaluate_Expression_Band_Related_Content_Reference_ID"
>EVALUATE_EXPRESSION_BAND_CALCULATED_FIELD_RELATED_CONTENT-6-2d405a8ea1ea01265d95c04def2d96c7</wd:ID>
</wd:Payroll>
<wd:Tax_Reporting_Unit>SJHNC, LLC</wd:Tax_Reporting_Unit>
<wd:Company_ID>G123</wd:Company_ID>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:Country_Code>US</wd:Country_Code>
<wd:Entity_Name>BalanceInitialization</wd:Entity_Name>
<wd:Legislative_Data_Group_Name>US Legislative Data Group</wd:Legislative_Data_Group_Name>
<wd:Legal_Entity_Name wd:Descriptor="SRMH">
<wd:ID wd:type="WID">66b06c2b4ec001df7d2d7875ea020e52</wd:ID>
<wd:ID wd:type="Organization_Reference_ID">G123</wd:ID>
<wd:ID wd:type="Company_Reference_ID">G123</wd:ID>
</wd:Legal_Entity_Name>
<wd:Person_Number>2110013</wd:Person_Number>
<wd:Effective_Date>2020-01-01</wd:Effective_Date>
<wd:Period>2020-Q1</wd:Period>
<wd:Dimension>Assignment Tax Unit Quarter to Date</wd:Dimension>
<wd:Pay_Component_Name>420 Time Off - PTO</wd:Pay_Component_Name>
<wd:Pay_Component_Code>420</wd:Pay_Component_Code>
<wd:Number>1623.96</wd:Number>
<wd:Payroll wd:Descriptor="PSJH Biweekly B">
<wd:ID wd:type="WID">2d405a8ea1ea01265d95c04def2d96c7</wd:ID>
<wd:ID wd:type="Evaluate_Expression_Band_Related_Content_Reference_ID"
>EVALUATE_EXPRESSION_BAND_CALCULATED_FIELD_RELATED_CONTENT-6-2d405a8ea1ea01265d95c04def2d96c7</wd:ID>
</wd:Payroll>
<wd:Tax_Reporting_Unit>SJHNC, LLC</wd:Tax_Reporting_Unit>
<wd:Company_ID>G123</wd:Company_ID>
</wd:Report_Entry>
XSLT:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wd="urn:com.workday.report/ERP_PAY_CR_PAYROLL_BALANCES_LINE_AMT">
<xsl:output method="text"/>
<xsl:variable name="linefeed" select="'
'"/>
<xsl:variable name="pipe" select="'|'"/>
<xsl:param name="quote">"</xsl:param>
<xsl:variable name="payCodes">
<payCode name="Admin Leave Paid">
<legacyCode>400</legacyCode>
<legacyCode>432</legacyCode>
<payCode name="Regular">
<legacyCode>101</legacyCode>
<legacyCode>102</legacyCode>
<legacyCode>103</legacyCode>
<legacyCode>112</legacyCode>
<legacyCode>800</legacyCode>
<legacyCode>964</legacyCode>
</payCode>
<payCode name="Overtime Weekly">
<legacyCode>126</legacyCode>
</payCode>
<payCode name="Overtime Weekly FLSA">
<legacyCode>123</legacyCode>
</payCode>
</payCode>
<payCode name="Bereavement">
<legacyCode>410</legacyCode>
<legacyCode>436</legacyCode>
</payCode>
<payCode name="Break Penalty">
<legacyCode>777</legacyCode>
</payCode>
<payCode name="PTO Donation">
<legacyCode>413</legacyCode>
<legacyCode>420</legacyCode>
<legacyCode>422</legacyCode>
</payCode>
<payCode name="CA Healthy Family PTO">
<legacyCode>496</legacyCode>
<legacyCode>497</legacyCode>
<legacyCode>498</legacyCode>
<legacyCode>499</legacyCode>
</payCode>
<payCode name="Callback 1 5">
<legacyCode>153</legacyCode>
</payCode>
</xsl:variable>
<xsl:key name="paycode" match="payCode" use="legacyCode" />
<xsl:template match="/wd:Report_Data">
<!-- <xsl:variable name="common">
<xsl:value-of select="wd:Report_Entry/wd:Legal_Entity_Name/#wd:Descriptor"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="wd:Report_Entry/wd:Person_Number"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="wd:Report_Entry/wd:Calendar_Quarter/#wd:Descriptor"/>
<xsl:text>|</xsl:text>
</xsl:variable> -->
<!-- HEADER LINE 1-->
<xsl:text>Country Code|Entity Name|Legislative Data Group Name|Legal Entity Name|Person Number|Effective As Of Date|Assignment Number|Value|Upload Date|Balance|Dimension|Payroll Relationship Number|Term Number|Assignment Number|Legal Employer|Payroll|Area One|Area Two|Area Three|Area Four|Third Party Payee|Time Definition|Calculation Breakdown|Balance Date|Tax Reporting Unit|Run Type|Context One|Context One Value|Context Two|Context Two Value|Context Three|Context Three Value|Context Four|Context Four Value|Context Five|Context Five Value|Context Six|Context Six Value
</xsl:text>
<!-- HEADER LINE 2-->
<xsl:text>COUNTRY_CODE|ENTITY_NAME|LEGISLATIVE_DATA_GROUP|LEGAL_ENTITY_NAME|PERSON_NUMBER|EFFECTIVE_DATE|ASSIGNMENT_NUMBER|POSITION2|POSITION3|POSITION4|POSITION5|POSITION6|POSITION7|POSITION8|POSITION9|POSITION10|POSITION11|POSITION12|POSITION13|POSITION14|POSITION15|POSITION16|POSITION17|POSITION18|POSITION19|POSITION20|POSITION21|POSITION22|POSITION23|POSITION24|POSITION25|POSITION26|POSITION27|POSITION28|POSITION29|POSITION30|POSITION31|POSITION32
</xsl:text>
<!-- HEADER LINE 3 -->
<xsl:text>VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|DATE|VARCHAR2|NUMBER|DATE|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|DATE|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2|VARCHAR2
</xsl:text>
<!-- DATA -->
<xsl:for-each-group select="wd:Report_Entry" group-by="concat(key('paycode', wd:Pay_Component_Code, $payCodes),wd:Person_Number,wd:Effective_Date)">
<xsl:if test="exists(key('paycode', wd:Pay_Component_Code, $payCodes)/#name)">
<xsl:value-of select="wd:Country_Code"/>
<xsl:value-of select="$pipe"/>
<xsl:value-of select="wd:Entity_Name"/>
<xsl:value-of select="$pipe"/>
<xsl:value-of select="wd:Legislative_Data_Group_Name"/>
<xsl:value-of select="$pipe"/>
<xsl:value-of select="wd:Legal_Entity_Name/#wd:Descriptor"/>
<xsl:value-of select="$pipe"/>
<xsl:value-of select="wd:Person_Number"/>
<xsl:value-of select="$pipe"/>
<xsl:value-of select="wd:Effective_Date"/>
<xsl:value-of select="$pipe"/>
<!-- Assginment Number Place Holder -->
<xsl:text>|</xsl:text>
<xsl:value-of select="format-number(sum(current-group()/wd:Number),'#.00')"/>
<xsl:value-of select="$pipe"/>
<!-- Upload Date Place Holder -->
<xsl:value-of select="wd:Effective_Date"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('paycode', wd:Pay_Component_Code, $payCodes)/#name"/>
<xsl:value-of select="$pipe"/>
<!-- Dimension
<xsl:choose>
<xsl:when test="exists(wd:Earning)">
<xsl:value-of select="concat('Assignment Tax Unit',' ',substring-before(wd:Dimension/#wd:Descriptor,' 20'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('Relationship Tax',' ',substring-before(wd:Dimension/#wd:Descriptor,' 20'))"/>
</xsl:otherwise>
</xsl:choose> -->
<xsl:value-of select="wd:Dimension"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="wd:Person_Number"/>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:value-of select="wd:Payroll/#wd:Descriptor"/>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<!-- Balance Date Place Holder -->
<xsl:value-of select="wd:Effective_Date"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="wd:Tax_Reporting_Unit"/>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>|</xsl:text>
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
I don't see a line 1786 in your stylesheet, but assuming that the problem is with
group-by="concat(key('paycode', wd:Pay_Component_Code, $payCodes),
wd:Person_Number, wd:Effective_Date)">
it's complaining about the first argument to concat(), saying that it contains a sequence of two payCode elements. But even if it only selected one, this code would be questionable, because the element selected by the key() function would be atomized to get its string value, and the string value of a payCode element isn't anything very meaningful. Perhaps it should be key(...)/#name?
The problem seems to be with your data, not with your code; or rather, it's with the assumptions your code is making about your data, in particular uniqueness of values. I would therefore suggest that the solution lies in validation; define your data more precisely, e.g. with an XSD schema, and validate it before you apply the transformation. Alternatively, you can do a lot of validation with custom XSLT code.
I have an issue with the XSLT below. I need help to fix my transformation. I am using XSLT 1.0. Input can be 120KVA or 120MVA or 120.0KVA or 120.0KV. Output i want to parse into 3 parts. i.e.
<tns:ratedApparentPower>
<tns:unitSymbolUnit>VA</tns:unitSymbolUnit>
<tns:multiplier>K</tns:multiplier>
<tns:floatValue>120.0</tns:floatValue>
</tns:ratedApparentPower>
My current Transformation is:
<tns:ratedApparentPower>
<tns:unitSymbolUnit>VA</tns:unitSymbolUnit>
<tns:multiplier>
<xsl:value-of select="substring(translate(//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE,'1234567890', ''),1,1)" />
</tns:multiplier>
<tns:floatValue>
<xsl:value-of select="substring-before(//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE,substring(translate(//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE,'1234567890', ''),1,1))" />
</tns:floatValue>
</tns:ratedApparentPower>
</xsl:if>
Generated O/P:
<tns:ratedApparentPower>
<tns:unitSymbolUnit>VA</tns:unitSymbolUnit>
<tns:multiplier>.</tns:multiplier>
<tns:floatValue>120</tns:floatValue>
</tns:ratedApparentPower>
My doubts are:
How to get <tns:unitSymbolUnit>VA</tns:unitSymbolUnit>? Currently, I am hardcoding it. But it can be V or VA or any other value
How to get multiplier? With my current logic I get as <tns:multiplier>.</tns:multiplier> when I have 120.0 it works fine.
With my current logic I get <tns:floatValue>120</tns:floatValue> instead of 120.0.
Is there any way I can shorten the path (//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE) by assigning it to some variable instead of using whole path every time?
Michael I edited your template belwo to match my requiremnts and usinga callTemplate but receive empty response
<xsl:template name="convertFloatValues">
<xsl:param name="floatValue1"/>
<xsl:variable name="unit" select="translate($floatValue1, '0123456789.', '')"/>
<unitSymbolUnit>
<xsl:value-of select="substring($unit, 2)"/>
</unitSymbolUnit>
<multiplier>
<xsl:value-of select="substring($unit, 1 , 1)"/>
</multiplier>
<floatValue>
<xsl:value-of select="substring-before(., $unit)"/>
</floatValue>
</xsl:template>
Call Template example . Not sure what I am missing. Can you please help me.
<tns:ratedApparentPower>
<xsl:call-template name="convertFloatValues">
<xsl:with-param name="floatValue1" select="/ns0:OutputParameters/ns0:XXJEAM_ASSET_SEARCH_PKG-24GETAS/ns0:ATTRIBUTE_GROUPS/ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE"/>
</xsl:call-template>
</tns:ratedApparentPower>
Input:
<OutputParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="*****">
<XXJEAM_ASSET_SEARCH_PKG-24GETAS>
<ATTRIBUTE_GROUPS_ITEM>
<NAME>DISTXFR</NAME>
<ATTRIBUTES>
<ATTRIBUTES_ITEM>
<COLUMN_NAME>C_ATTRIBUTE3</COLUMN_NAME>
<NAME>Volt</NAME>
<VALUE>500</VALUE>
</ATTRIBUTES_ITEM>
<ATTRIBUTES_ITEM>
<COLUMN_NAME>C_ATTRIBUTE4</COLUMN_NAME>
<NAME>KVA</NAME>
<VALUE>500.0KVA</VALUE>
</ATTRIBUTES_ITEM>
</ATTRIBUTES>
</ATTRIBUTE_GROUPS_ITEM>
</XXJEAM_ASSET_SEARCH_PKG-24GETAS>
</OutputParameters>
Consider the following example:
XML
<input>
<item>20KVA</item>
<item>120MVA</item>
<item>120.0KVA</item>
<item>120.0KV</item>
</input>
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:template match="/input">
<output>
<xsl:for-each select="item">
<xsl:variable name="unit" select="translate(., '0123456789.', '')" />
<ratedApparentPower>
<unitSymbolUnit>
<xsl:value-of select="substring($unit, 2)" />
</unitSymbolUnit>
<multiplier>
<xsl:value-of select="substring($unit, 1 , 1)" />
</multiplier>
<floatValue>
<xsl:value-of select="substring-before(., $unit)" />
</floatValue>
</ratedApparentPower>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<ratedApparentPower>
<unitSymbolUnit>VA</unitSymbolUnit>
<multiplier>K</multiplier>
<floatValue>20</floatValue>
</ratedApparentPower>
<ratedApparentPower>
<unitSymbolUnit>VA</unitSymbolUnit>
<multiplier>M</multiplier>
<floatValue>120</floatValue>
</ratedApparentPower>
<ratedApparentPower>
<unitSymbolUnit>VA</unitSymbolUnit>
<multiplier>K</multiplier>
<floatValue>120.0</floatValue>
</ratedApparentPower>
<ratedApparentPower>
<unitSymbolUnit>V</unitSymbolUnit>
<multiplier>K</multiplier>
<floatValue>120.0</floatValue>
</ratedApparentPower>
</output>
In an input XML file, along with Static Columns, columns expecting data from other files (reference)is also available.
But for each reference, the input xml has separate row with same ID or UID.
The output file has to have all references and relations in one row (based on the ID or UID)
I wrote the XSLT for this transformation also. This XSLT is faster when the row count is less (< 100 or < 200). But, as the count grows, the output xml generation taking long time (for count of 1000 rows, around 30 mins).
I am using
<xsl:for-each select="z:row/#ID[generate-id() = generate-id(key('UniqueID',.))]">
in the XSLT. Because for the same ID in each row of input xml, it has to check for multiple references (like section) and relations (like Child) and populate the same as columns
Input Raw XML File.
<xml xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:z="#RowsetSchema">
<rs:data>
<z:row UID="PARENT_001_1221AD_A878" GroupID="" GroupRel="" ID="37" Name="Outer Asset Details" RelProduct="Line1" RelUID="CHILD1_101_9899_9POOU99" RelName="CHILD1" RelType="Child" Size="22"/>
<z:row UID="PARENT_001_1221AD_A878" GroupID="" GroupRel="" ID="37" Name="Outer Asset Details" RelProduct="Line1" RelUID="CHILD2_201_5646546_9890PBS" RelName="CHILD1" RelType="Child" Size="22"/>
<z:row UID="PARENT_001_1221AD_A878" GroupID="" GroupRel="" ID="37" Name="Outer Asset Details" RelProduct="Line1" RelUID="SEC_999_99565_998AFSD" RelName="Hydraulic Section" RelType="Section" Size="22"/>
</rs:data>
Child.xml
<Child xsi:noNamespaceSchemaLocation="../XSD/Child.xsd" FILE="Child" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Row UID="CHILD1_101_9899_9POOU99">
<Name>CHILD1</Name>
<Description>This has details about the Hydraulic sections of the automobile</Description>
</Row>
<Row UID="CHILD2_201_5646546_9890PBS">
<Name>CHILD2</Name>
<Description>This has details about the manual sections of the automobile</Description>
</Row>
Section.xml
<Section xsi:noNamespaceSchemaLocation="../XSD/Section.xsd" FILE="Section" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Row UID="SEC_999_99565_998AFSD">
<Name>Hydraulic Section</Name>
<Description>This has details about the Sections in which the Hydraulic Systems are used.</Description>
</Row>
XSLT File
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema" exclude-result-prefixes="s dt z rs msxsl" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="UniqueID" match="z:row/#ID" use="."/>
<xsl:template match="/">
<Parent xsi:noNamespaceSchemaLocation="../XSD/Parent.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" FILE="Parent">
<xsl:for-each select="xml">
<xsl:apply-templates select="rs:data"/>
</xsl:for-each>
</Parent>
</xsl:template>
<xsl:template match="rs:data">
<xsl:for-each select="z:row/#ID[generate-id() = generate-id(key('UniqueID',.))]">
<xsl:variable name="FRId">
<xsl:value-of select="current()"/>
</xsl:variable>
<xsl:variable name="curNSet" select="//z:row[#ID=$FRId]"/>
<xsl:copy-of select="current()"/>
<Record>
<xsl:attribute name="UID"><xsl:value-of select="$curNSet/#UID"/></xsl:attribute>
<xsl:element name="Size">
<xsl:value-of select="$curNSet/#Size"/>
</xsl:element>
<xsl:element name="Child">
<xsl:apply-templates select="$curNSet[#RelType='Child']" mode="Relations">
<xsl:with-param name="RelType" select="'Child'"/>
<xsl:with-param name="DstFileName" select="'../Files/Child.xml'"/>
</xsl:apply-templates>
</xsl:element>
<xsl:element name="Section">
<xsl:apply-templates select="$curNSet[#RelType='Section']" mode="References">
<xsl:with-param name="RelType" select="'Section'"/>
<xsl:with-param name="DstFileName" select="'../Files/Section.xml'"/>
</xsl:apply-templates>
</xsl:element>
</Record>
</xsl:for-each>
</xsl:template>
<xsl:template match="z:row" mode="Relations">
<xsl:param name="RelType"/>
<xsl:param name="DstFileName"/>
<xsl:element name="{$RelType}">
<xsl:attribute name="DestinationKey"><xsl:value-of select="#RelUID"/></xsl:attribute>
<xsl:attribute name="RelFilePath"><xsl:value-of select="$DstFileName"/></xsl:attribute>
<xsl:attribute name="SequenceNumber"><xsl:value-of select="position()"/></xsl:attribute>
<xsl:value-of select="#RelName"/>
</xsl:element>
</xsl:template>
<xsl:template match="z:row" mode="References">
<xsl:param name="DstFileName"/>
<xsl:attribute name="DestinationKey"><xsl:value-of select="#RelUID"/></xsl:attribute>
<xsl:attribute name="RelFilePath"><xsl:value-of select="$DstFileName"/></xsl:attribute>
<xsl:attribute name="SequenceNumber"><xsl:value-of select="position()"/></xsl:attribute>
<xsl:value-of select="#RelName"/>
</xsl:template>
Output.xml
<Parent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../XSD/Parent.xsd" FILE="Parent" ID="37">
<Record UID="PARENT_001_1221AD_A878">
<Size>22</Size>
<Child>
<Child DestinationKey="CHILD1_101_9899_9POOU99" RelFilePath="../Files/Child.xml" SequenceNumber="1">CHILD1</Child>
<Child DestinationKey="CHILD2_201_5646546_9890PBS" RelFilePath="../Files/Child.xml" SequenceNumber="2">CHILD1</Child>
</Child>
<Section DestinationKey="SEC_999_99565_998AFSD" RelFilePath="../Files/Section.xml" SequenceNumber="1">Hydraulic Section</Section>
</Record>
Please help me in optimizing the XSLT, so that the output file is generated faster
Consider to use
<xsl:key name="UniqueID" match="z:row" use="#ID"/>
then
<xsl:for-each select="z:row/#ID[generate-id() = generate-id(key('UniqueID',.))]">
<xsl:variable name="FRId">
<xsl:value-of select="current()"/>
</xsl:variable>
<xsl:variable name="curNSet" select="//z:row[#ID=$FRId]"/>
can be replaced with
<xsl:for-each select="z:row[generate-id() = generate-id(key('UniqueID', #ID))]">
<xsl:variable name="FRId" select="#ID"/>
<xsl:variable name="curNSet" select="key('UniqueID', #ID"/>
I am not sure you need the variable FRId at all but defining it with a select attribute instead of a nested value-of is certainly consuming less resources.
To make
<xsl:apply-templates select="$curNSet[#RelType='Child']" mode="Relations">
more efficient define a key
<xsl:key name="rel" match="z:row" use="concat(#ID, '|', #RelType)"/>
then use
<xsl:apply-templates select="key('rel', concat(#ID, '|', 'Child')" mode="Relations">
Then use the same approach for the other apply-templates.
All of the above is untested but should give you an idea.
I have to transform a XML into CSV, however the transformation is quite difficult for me.
This is a sample XML:
<?xml version="1.0" encoding="windows-1251"?>
<message text="Contract [number] of [sum] [currency] till [deadline]" report="someone#lost.com">
<customer mobile="X69931232">
<contract number="FL1-22/Ml">
<sum>21,55</sum>
<currency>USD</currency>
<deadline>30.09.2011</deadline>
</contract>
</customer>
<customer mobile="X79484483">
<contract number="FL1-24">
<sum>329,44</sum>
<currency>EUR</currency>
<deadline>30.12.2011</deadline>
</contract>
<contract number="FL1-27">
<sum>232,91</sum>
<currency>EUR</currency>
<deadline>30.12.2011</deadline>
</contract>
</customer>
<customer mobile="X69502060, X79484483">
<contract number="FL1-07">
<sum>42,17</sum>
<currency>USD</currency>
<deadline>30.09.2011</deadline>
</contract>
</customer>
<customer mobile="X69931232, X79484483">
<contract number="FL2-01/M2">
<sum>40,84</sum>
<currency>EUR</currency>
<deadline>30.09.2011</deadline>
</contract>
<contract number="FL1-18">
<sum>198,45</sum>
<currency>EUR</currency>
<deadline>30.11.2011</deadline>
</contract>
</customer>
</message>
And the sample CSV should look like:
X69931232,Contract FL1-22/Ml sum of 21,55 USD till 30.09.2011
X79484483,Contract FL1-24 sum of 329,44 EUR till 30.12.2011; FL1-27 sum of 232,91 EUR till 30.09.2011
X69502060,Contract FL1-07 sum of 42,17 USD till 30.09.2011
X79484483,Contract FL1-07 sum of 42,17 USD till 30.09.2011
X69931232,Contract FL2-01/M2 sum of 40,84 EUR till 30.09.2011; FL1-18 sum of 198,45 EUR till 30.11.2011
X79484483,Contract FL2-01/M2 sum of 40,84 EUR till 30.09.2011; FL1-18 sum of 198,45 EUR till 30.11.2011
My current XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
Phone,Message
<xsl:for-each select="/message/customer">
<xsl:sort order="ascending" select="contract/#number"/>
<xsl:value-of select="/message/customer/#mobile"/>,
Contract <xsl:value-of select="contract/#number"/> sum of <xsl:value-of select="contract/sum"/>
<xsl:value-of select="contract/currency"/> till <xsl:value-of select="contract/deadline"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
If the mobile attribute contains more than one number, than the text should be the same for both;
If there are more than one contract per customer, send as a new line. In the sample file I showed every possible variation.
Thank you in advance!!!
This would be easier in XSLT2, but since you started in 1, here's the old way with a recursive split template:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:for-each select="/message/customer/contract">
<xsl:sort order="ascending" select="#number"/>
<xsl:call-template name="split">
<xsl:with-param name="m" select="../#mobile"/>
<xsl:with-param name="r">
<xsl:text>, Contract </xsl:text>
<xsl:value-of select="#number"/>
<xsl:text> sum of </xsl:text>
<xsl:value-of select="sum"/>
<xsl:value-of select="currency"/>
<xsl:text> till </xsl:text>
<xsl:value-of select="deadline"/>
<xsl:text>
</xsl:text>
</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="split">
<xsl:param name="m"/>
<xsl:param name="r"/>
<xsl:choose>
<xsl:when test="contains($m,',')">
<xsl:value-of select="normalize-space(substring-before($m,','))"/>
<xsl:value-of select="$r"/>
<xsl:call-template name="split">
<xsl:with-param name="m" select="substring-after($m,',')"/>
<xsl:with-param name="r" select="$r"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space($m)"/>
<xsl:value-of select="$r"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
producing
$ saxon csv1.xml csv2.xsl
X69502060, Contract FL1-07 sum of 42,17USD till 30.09.2011
X79484483, Contract FL1-07 sum of 42,17USD till 30.09.2011
X69931232, Contract FL1-18 sum of 198,45EUR till 30.11.2011
X79484483, Contract FL1-18 sum of 198,45EUR till 30.11.2011
X69931232, Contract FL1-22/Ml sum of 21,55USD till 30.09.2011
X79484483, Contract FL1-24 sum of 329,44EUR till 30.12.2011
X79484483, Contract FL1-27 sum of 232,91EUR till 30.12.2011
X69931232, Contract FL2-01/M2 sum of 40,84EUR till 30.09.2011
X79484483, Contract FL2-01/M2 sum of 40,84EUR till 30.09.2011