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

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.

Related

XSLT: Trying to combine two nodes based on the value of an element of those two nodes

I am struggling with XSLT and have been for days. I'm in the process of modifying a previous coworkers python code that transforms many different JSON files into xml and finally into kml. I'm within a hairs breadth of wrapping this up and, of course, the one part I can't get my head around is what's left.
I have an xml file with this structure:
<?xml version="1.0" ?>
<document xmlns="http://ws.wso2.org/dataservice">
<group>
<display_name>Housing</display_name>
<id>Housing</id>
<item>
<id>5063</id>
<image_url>images/5063.jpg</image_url>
<latitude>40.354007</latitude>
<longitude>-74.666675</longitude>
<name>Stanworth Apartments</name>
</item>
.
. (Many items omitted)
.
</group>
<group>
<display_name>Buildings</display_name>
<id>Building</id>
<item>
<id>5025</id>
<image_url>images/5025.jpg</image_url>
<latitude>40.350066</latitude>
<longitude>-74.603464</longitude>
<name>Lyman Spitzer Building</name>
<name_alt>LSB</name_alt>
<organization_id>ORG418</organization_id>
</item>
.
. (Many items omitted)
.
</group>
<group>
.
. (Many groups omitted)
.
</group>
<group>
<display_name>Accessible Features</display_name>
<id>Entryway</id>
<item>
<description>Accessible entryway</description>
<id>E028</id>
<latitude>40.349159</latitude>
<longitude>-74.658629</longitude>
<name>E028</name>
</item>
<item>
<description>Accessible entryway</description>
<id>E029</id>
<latitude>40.349398</latitude>
<longitude>-74.658517</longitude>
<name>E029</name>
</item>
</group>
<group>
<display_name>Accessible Features</display_name>
<id>Route</id>
<item>
<description>Accessible pathway</description>
<id>R054</id>
<name>R054</name>
<steps>-74.66032495749012,40.3489269473544</steps>
<steps>-74.6602836233495,40.34888813533125</steps>
</item>
<item>
<description>Accessible pathway</description>
<id>R055</id>
<name>R055</name>
<steps>-74.66023036637355,40.34884827131961</steps>
<steps>-74.66018651597699,40.34881015960344</steps>
</item>
<item>
<description>Accessible pathway</description>
<id>R072</id>
<name>R072</name>
<steps>-74.66101885775542,40.34737535360176</steps>
<steps>-74.6610915120654,40.34740600913134</steps>
<steps>-74.66187000551304,40.34717392492537</steps>
</item>
</group>
</document>
Each "group" is transformed into a Folder in the final KML file.
<Folder id="Housing">
<name>Housing</name>
<Placemark id="_0288">
.
. (Many lines omitted)
.
The goal is to create one Folder "id='Accessible" with the contents of two groups. The group with id='Entryway' and the group with id='Route. The desired output would be:
<Folder id="Accessible">
<name>Accessible Features</name>
<Placemark id="_E001">
<name>E001</name>
<description><![CDATA[<div><p>Accessible entryway</p></div>]]></description>
<styleUrl>#entryway</styleUrl>
<Point>
<coordinates>-74.663266, 40.348289,0</coordinates>
</Point>
</Placemark>
<Placemark id="_E002">
<name>E002</name>
<description><![CDATA[<div><p>Accessible entryway</p></div>]]></description>
<styleUrl>#entryway</styleUrl>
<Point>
<coordinates>-74.662252, 40.348057,0</coordinates>
</Point>
</Placemark>
.
. then have the items from the group with id='Route'
.
<Placemark id="_R002">
<name>Accessible Routes</name>
<description><![CDATA[<div><p>Accessible pathway</p></div>]]></description>
<styleUrl/>
<Style>
<LineStyle>
<color>FFFF0000</color>
<width>4</width>
</LineStyle>
</Style>
<LineString>
<coordinates>
-74.65135187837255,40.34699608960065
-74.65134698312161,40.34698651192196
</coordinates>
</LineString>
</Placemark>
<Placemark id="_R003">
<name>Accessible Routes</name>
<description><![CDATA[<div><p>Accessible pathway</p></div>]]></description>
<styleUrl/>
<Style>
<LineStyle>
<color>FFFF0000</color>
<width>4</width>
</LineStyle>
</Style>
<LineString>
<coordinates>
-74.65135184561255,40.34699603789065
-74.65134698312256 44.34698634192100
</coordinates>
</LineString>
</Placemark>
.
. more than 66,000 lines omitted
.
</Folder>
I've written the XSLT to transform the XML into these KML Folders and the only thing left to do is get them under the same folder.
What I've been trying to do is move all of the items from the group with id='Route' into the group with id='Entryway.
In my xslt file is an apply-templates at the group nodes.
<xsl:apply-templates select="ds:group">
<xsl:sort select="ds:display_name"/>
</xsl:apply-templates>
This is picked up by the template match for each group.
<xsl:template match="ds:group">
At which point I'm lost. I'll post my code but it is only going to confuse and depress you.
<xsl:template match="ds:group">
<xsl:choose>
<xsl:when test="not(ds:id = 'Route') and not(ds:id = 'Entryway')">
<Folder id="{ds:id}">
<name>
<xsl:value-of select="ds:display_name"/>
</name>
<xsl:apply-templates select="ds:item">
<xsl:sort select="ds:name"/>
</xsl:apply-templates>
</Folder>
</xsl:when>
<xsl:when test="ds:id = 'Entryway'">
<Folder id='Accessible'>
<name>
<xsl:value-of select="ds:display_name"/>
</name>
<xsl:apply-templates select="ds:item">
<xsl:sort select="ds:name"/>
</xsl:apply-templates>
</Folder>
</xsl:when>
<xsl:when test="ds:id = 'Route'">
<!-- Copy all of current node to Entryway node -->
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I don't think I'm going about this the right way. By the time the XSLT process gets to the group with id='Route' the KML Folder for Entryway has already been written. I'm at a dead end. Can I union two groups together based on the value of "id"? Conceptually the idea would be: <xsl:template match="ds:id='Route' | ds:id='Entryway'"> But that doesn't even compile.
Can I copy all of the elements of the group (id='Route') to the group (id='Entryway') after the first group has been processed?
Thank you in advance for your time and attention.
George
I think you need to "step" in at the document level and do e.g.
<xsl:template match="ds:document">
<Folder id='Accessible'>
<xsl:apply-templates select="ds:group[ds:id = 'Entryway' or ds:id = 'Route']"/>
</Folder>
<!-- process other ds:group here as well e.g.
<xsl:apply-templates select="ds:group[not(ds:id = 'Entryway' or ds:id = 'Route')]"/>
-->
</xsl:template>
AFAICT you want to do something like:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://ws.wso2.org/dataservice"
exclude-result-prefixes="ns0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/ns0:document">
<Folders>
<xsl:variable name="accessible" select="ns0:group[ns0:id='Entryway' or ns0:id ='Route']" />
<xsl:if test="$accessible">
<Folder id="Accessible">
<name>Accessible Features</name>
<xsl:apply-templates select="$accessible/ns0:item"/>
</Folder>
</xsl:if>
<xsl:apply-templates select="ns0:group[not(ns0:id='Entryway' or ns0:id ='Route')]"/>
</Folders>
</xsl:template>
<xsl:template match="ns0:group">
<Folder id="{ns0:id}">
<name>
<xsl:value-of select="display_name"/>
</name>
<xsl:apply-templates select="ns0:item"/>
</Folder>
</xsl:template>
<xsl:template match="ns0:item">
<Placemark id="{ns0:id}">
<name>
<xsl:value-of select="ns0:name"/>
</name>
<!-- more here -->
</Placemark>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="/ds:document">
<kml>
<Document>
<name>Campus Map</name>
<xsl:apply-templates select="ds:group[ds:id != 'Entryway' and ds:id != 'Route']">
<xsl:sort select="ds:display_name"/>
</xsl:apply-templates>
<Folder id='Accessible'>
<name>Accessible Feature</name>
<xsl:apply-templates select="ds:group[ds:id = 'Entryway' or ds:id = 'Route']"/>
</Folder>
</Document>
</kml>
</xsl:template>
<xsl:template match="ds:group[ds:id = 'Entryway' or ds:id = 'Route']">
<xsl:apply-templates select="ds:item"/>
</xsl:template>
<xsl:template match="ds:group[ds:id != 'Entryway' and ds:id != 'Route']">
<Folder id="{ds:id}">
<name>
<xsl:value-of select="ds:display_name"/>
</name>
<xsl:apply-templates select="ds:item">
<xsl:sort select="ds:name"/>
</xsl:apply-templates>
</Folder>
</xsl:template>

XSLT 1.0 to retrieve or parse a specific value

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>

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>

Namespace issue in xslt

I am trying to convert following xml into other xml but I am not getting the values for xCoordinate and yCoordinate. I would like to convert the structure from source - XML to Target-XML where the goocodes will match and the result would be assigned to x and y.
Source - XML
<?xml version="1.0"?>
<AddressResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" errorCode="0" errorDescription="">
<wrappedResultList xmlns="http://xlocate.xserver.ptvag.com">
<ResultAddress city="Amsterdam" city2="" country="NL" houseNumber="" postCode="1***" state="Noord-Holland" street="" adminRegion="Amsterdam" appendix="" classificationDescription="EXACT" countryCapital="Amsterdam" detailLevelDescription="CITY" totalScore="100">
<wrappedAdditionalFields />
<coordinates>
<kml xsi:nil="true" xmlns="http://common.xserver.ptvag.com" />
<point x="4.89327999999999" y="52.373090000000005" xmlns="http://common.xserver.ptvag.com" />
</coordinates>
</ResultAddress>
<ResultAddress city="Amsterdam-Zuidoost" city2="" country="NL" houseNumber="" postCode="110*" state="Noord-Holland" street="" adminRegion="Amsterdam" appendix="" classificationDescription="EXACT" countryCapital="Amsterdam" detailLevelDescription="CITY" totalScore="80">
<wrappedAdditionalFields />
<coordinates>
<kml xsi:nil="true" xmlns="http://common.xserver.ptvag.com" />
<point x="4.9513699999999838" y="52.316199999999988" xmlns="http://common.xserver.ptvag.com" />
</coordinates>
</ResultAddress>
<ResultAddress city="Nieuw-Amsterdam" city2="" country="NL" houseNumber="" postCode="7833" state="Drenthe" street="" adminRegion="Emmen" appendix="" classificationDescription="EXACT" countryCapital="Amsterdam" detailLevelDescription="CITY" totalScore="80">
<wrappedAdditionalFields />
<coordinates>
<kml xsi:nil="true" xmlns="http://common.xserver.ptvag.com" />
<point x="6.8528699999999994" y="52.716139999999982" xmlns="http://common.xserver.ptvag.com" />
</coordinates>
</ResultAddress>
</wrappedResultList>
</AddressResponse>
Target - XML
<GeoCodeResponse>
<geocordinate>
<xCordinate>4.89327999999999</xCordinate>
<yCordinate>52.716139999999982</yCordinate>
</geocordinate>
</GeoCodeResponse>
XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xl="http://xlocate.xserver.ptvag.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance" xmlns:cm="http://common.xserver.ptvag.com" exclude-result-prefixes="xl xsi xsd cm" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<GeoCodeResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<geocordinate xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<xsl:for-each select="AddressResponse/xl:wrappedResultList/xl:ResultAddress">
<xsl:sort select="#xl:totalScore" order="descending" data-type="number"/>
<xsl:if test="position()= 1">
<xCordinate> <xsl:value-of select="/xl:coordinates/cm:point/cm:x" /></xCordinate>
<yCordinate> <xsl:value-of select="/xl:coordinates/cm:point/cm:y" /></yCordinate>
</xsl:if>
</xsl:for-each>
</geocordinate>
</GeoCodeResponse>
</xsl:template>
</xsl:stylesheet>
Please help what could be done in above xslt.
You were almost there. The coordinates are attributes, not nodes.
change it into this:
<xsl:if test="position()= 1">
<xCordinate>
<xsl:value-of select="xl:coordinates/cm:point/#x" />
</xCordinate>
<yCordinate>
<xsl:value-of select="xl:coordinates/cm:point/#y" />
</yCordinate>
</xsl:if>
You are also referencing the coordinates node from the root, but it should be relative. I changed /xl:coordinaties into xl:coordinates

How to Optimize XSLT for Referencing data in other XMLs

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.