I need to map field value from hash table only in Debtor field and other values remains same.
I want the all other fields to be as it is and only change the map the debtor fields.
P_BPMapping - {"BANK1":"1","BANK2":"2","BANK3":"3","BANK4":"4"}
My input xml
<?xml version="1.0" encoding="UTF-8"?>
<JECreateRequest>
<MessageHeader>
</MessageHeader>
<JournalEntryCreateRequest>
<MessageHeader>
<JournalEntry>
<DebtorItem>
<Debtor>BANK1</Debtor>
</DebtorItem>
</JournalEntry>
</MessageHeader>
</JournalEntryCreateRequest>
<JournalEntryCreateRequest>
<MessageHeader>
<JournalEntry>
<DebtorItem>
<Debtor>BANK2</Debtor>
</DebtorItem>
<DebtorItem>
<Debtor>BANK1</Debtor>
</DebtorItem>
</JournalEntry>
</MessageHeader>
</JournalEntryCreateRequest>
<JournalEntryCreateRequest>
<MessageHeader>
<JournalEntry>
<DebtorItem>
<Debtor>BANK4</Debtor>
</DebtorItem>
<DebtorItem>
<Debtor>BANK3</Debtor>
</DebtorItem>
<DebtorItem>
<Debtor>BANK1</Debtor>
</DebtorItem>
</JournalEntry>
</MessageHeader>
</JournalEntryCreateRequest>
</JournalEntryBulkCreateRequest>
Hash Table
BANK1 - 1
BANK2 - 2
BANK3 - 3
BANK4 - 4
My XSLT code - I have tried with this XSLT code and getting error.
<xsl:stylesheet
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Start of enhancement - ValueMapping for Payloution Customer -->
<xsl:param name="P_BPMapping" />
<xsl:param name="MapDebitor" as="map(*)" select="parse-json($P_BPMapping)" />
<!-- End of enhancement -->
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--
<xsl:for-each select ="MessageHeader/JournalEntryCreateRequest/JournalEntry/DebtorItem">
-->
<xsl:template match="MessageHeader">
<xsl:for-each select ="JournalEntryCreateRequest/JournalEntry/DebtorItem">
<xsl:template match="Debtor">
<xsl:copy>
<xsl:choose>
<xsl:when test="map:get($MapDebitor,Debtor)">
<xsl:value-of select="map:get($MapDebitor,Debtor)" />
</xsl:when>
<xsl:otherwise>
<error>
<xsl:text>Error in BusinessPartner mapping. Please maintain the mapping for the following debtor: </xsl:text>
<xsl:value-of select="Debtor"/>
</error>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:for-each>
</xsl:template>
<!--
</xsl:for-each>
-->
</xsl:stylesheet>
Expected output
<?xml version="1.0" encoding="UTF-8"?>
<JECreateRequest>
<MessageHeader>
</MessageHeader>
<JournalEntryCreateRequest>
<MessageHeader>
<JournalEntry>
<DebtorItem>
<Debtor>1</Debtor>
</DebtorItem>
</JournalEntry>
</MessageHeader>
</JournalEntryCreateRequest>
<JournalEntryCreateRequest>
<MessageHeader>
<JournalEntry>
<DebtorItem>
<Debtor>2</Debtor>
</DebtorItem>
<DebtorItem>
<Debtor>1</Debtor>
</DebtorItem>
</JournalEntry>
</MessageHeader>
</JournalEntryCreateRequest>
<JournalEntryCreateRequest>
<MessageHeader>
<JournalEntry>
<DebtorItem>
<Debtor>4</Debtor>
</DebtorItem>
<DebtorItem>
<Debtor>3</Debtor>
</DebtorItem>
<DebtorItem>
<Debtor>1</Debtor>
</DebtorItem>
</JournalEntry>
</MessageHeader>
</JournalEntryCreateRequest>
</JournalEntryBulkCreateRequest>
I would use e.g.
<xsl:param name="map-data" as="xs:string" expand-text="no">{"BANK1":"1","BANK2":"2","BANK3":"3","BANK4":"4"}</xsl:param>
<xsl:param name="map" select="parse-json($map-data)"/>
<xsl:template match="Debtor[map:contains($map, .)]">
<xsl:copy>{$map(.)}</xsl:copy>
</xsl:template>
<xsl:mode on-no-match="shallow-copy"/>
(with xmlns:map="http://www.w3.org/2005/xpath-functions/map" and expand-text="yes" being declared in the XSLT).
Your code also seems to try to find non-matching Debtors and output some error so perhaps an additional template
<xsl:template match="Debtor[not(map:contains($map, .))]">
<xsl:copy>
<error>Error in BusinessPartner mapping. Please maintain the mapping for the following debtor: {.}</error>
</xsl:copy>
</xsl:template>
is needed, though the input/output samples don't show that case.
Online sample demo.
Related
I have the following input XML and want to copy the "Delivery" Stop elements:
<?xml version="1.0" encoding="UTF-8" ?>
<LeanXML>
<ShipperLoadPlan>
<LoadNumber>129516728</LoadNumber>
<Stops>
<Stop>
<StopNumber>1</StopNumber>
<StopType>Pickup</StopType>
<Distance UOM="mi">0</Distance>
<CalcDueDate TimeZone="EDT" TimeZoneDesc="America/New_York">09/27/2021
00:00</CalcDueDate>
<ReferenceNums/>
<OrderNums>
<OrderNum ShipperRef="6000002014_30SAP" ShipmentLegID="291"
ShipmentLegSeq="1" ScheduleIntgKey="1">6000002014</OrderNum>
<OrderNum ShipperRef="6000002014_10SAP" ShipmentLegID="2916"
ShipmentLegSeq="1" ScheduleIntgKey="1">6000002014</OrderNum>
</OrderNums>
</Stop>
<Stop>
<StopNumber>2</StopNumber>
<StopType>Delivery</StopType>
<Distance UOM="mi">0</Distance>
<CalcDueDate TimeZone="CDT" TimeZoneDesc="America/Chicago">10/01/2021
00:00</CalcDueDate>
<ReferenceNums/>
<OrderNums>
<OrderNum ShipperRef="R6000002014_10SAP" ShipmentLegID="291608671"
ShipmentLegSeq="1" ScheduleIntgKey="1">6000002014</OrderNum>
</OrderNums>
</Stop>
<Stop>
<StopNumber>3</StopNumber>
<StopType>Delivery</StopType>
<Distance UOM="mi">473</Distance>
<CalcDueDate TimeZone="CDT" TimeZoneDesc="America/Chicago">10/03/2021
00:00</CalcDueDate>
<ReferenceNums/>
<OrderNums>
<OrderNum ShipperRef="R6000002014_30SAP" ShipmentLegID="291634632"
ShipmentLegSeq="1" ScheduleIntgKey="1">6000002014</OrderNum>
</OrderNums>
</Stop>
</Stops>
<DivertedOrders/>
<CompanyDefined/>
</ShipperLoadPlan>
</LeanXML>
I came up with this XSLT using a Key I put on the StopType = Pickup:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
exclude-result-prefixes="xs xd"
version="1.0">
<xsl:key name="keyOrderLineNum" match="Stop[StopType='Pickup']/OrderNums"
use="concat(OrderNum/#ShipperRef,'|',../../CalcDueDate,'|',../../StopNumber)"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Stop[StopType='Pickup']">
<xsl:copy>
<xsl:copy-of select="."/>
<CopiedStop>
<xsl:copy>
<xsl:value-of select="parent::Stops/Stop[StopType='Delivery']"/>
</xsl:copy>
</CopiedStop>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
But I am expecting this target XML, my bit question mark is how do I properly use my key to copy the Delivery Stop elements?
Expected Target:
<?xml version="1.0" encoding="UTF-8" ?>
<LeanXML>
<ShipperLoadPlan>
<LoadNumber>129516728</LoadNumber>
<Stops>
<Stop>
<StopNumber>1</StopNumber>
<StopType>Pickup</StopType>
<Distance UOM="mi">0</Distance>
<CalcDueDate TimeZone="EDT" TimeZoneDesc="America/New_York">09/27/2021
00:00</CalcDueDate>
<ReferenceNums/>
<OrderNums>
<OrderNum ShipperRef="6000002014_30SAP" ShipmentLegID="291"
ShipmentLegSeq="1" ScheduleIntgKey="1">6000002014</OrderNum>
<OrderNum ShipperRef="6000002014_10SAP" ShipmentLegID="2916"
ShipmentLegSeq="1" ScheduleIntgKey="1">6000002014</OrderNum>
</OrderNums>
<CopiedStop>
<Stop>
<StopNumber>2</StopNumber>
<StopType>Delivery</StopType>
<Distance UOM="mi">0</Distance>
<CalcDueDate TimeZone="CDT" TimeZoneDesc="America/Chicago">10/01/2021
00:00</CalcDueDate>
<ReferenceNums/>
<OrderNums>
<OrderNum ShipperRef="R6000002014_10SAP" ShipmentLegID="291608671"
ShipmentLegSeq="1" ScheduleIntgKey="1">6000002014</OrderNum>
</OrderNums>
</Stop>
<Stop>
<StopNumber>3</StopNumber>
<StopType>Delivery</StopType>
<Distance UOM="mi">473</Distance>
<CalcDueDate TimeZone="CDT" TimeZoneDesc="America/Chicago">10/03/2021
00:00</CalcDueDate>
<ReferenceNums/>
<OrderNums>
<OrderNum ShipperRef="R6000002014_30SAP" ShipmentLegID="291634632"
ShipmentLegSeq="1" ScheduleIntgKey="1">6000002014</OrderNum>
</OrderNums>
</Stop>
</CopiedStop>
</Stop>
<Stop>
<StopNumber>2</StopNumber>
<StopType>Delivery</StopType>
<Distance UOM="mi">0</Distance>
<CalcDueDate TimeZone="CDT" TimeZoneDesc="America/Chicago">10/01/2021
00:00</CalcDueDate>
<ReferenceNums/>
<OrderNums>
<OrderNum ShipperRef="R6000002014_10SAP" ShipmentLegID="291608671"
ShipmentLegSeq="1" ScheduleIntgKey="1">6000002014</OrderNum>
</OrderNums>
</Stop>
<Stop>
<StopNumber>3</StopNumber>
<StopType>Delivery</StopType>
<Distance UOM="mi">473</Distance>
<CalcDueDate TimeZone="CDT" TimeZoneDesc="America/Chicago">10/03/2021
00:00</CalcDueDate>
<ReferenceNums/>
<OrderNums>
<OrderNum ShipperRef="R6000002014_30SAP" ShipmentLegID="291634632"
ShipmentLegSeq="1" ScheduleIntgKey="1">6000002014</OrderNum>
</OrderNums>
</Stop>
</Stops>
<DivertedOrders/>
<CompanyDefined/>
</ShipperLoadPlan>
</LeanXML>
Can anyone help me please achieve this? I appreciate any tips on this.
AFAICT, you want to do something like:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="deiveryStops" match="Stop[StopType='Delivery']" use="OrderNums/OrderNum" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Stop[StopType='Pickup']">
<xsl:copy>
<xsl:copy-of select="*"/>
<CopiedStop>
<xsl:copy-of select="key('deiveryStops', OrderNums/OrderNum)"/>
</CopiedStop>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This ties the "Delivery" Stops to the "Pickup" Stop based on matching OrderNum alone.
I need to sum the multiplication of 2 numbers based on this example
<test>
<stop>
<id>1</id>
<unit_id>1</unit_id>
<unit_id>2</unit_id>
</stop>
<stop>
<id>2</id>
<unit_id>1</unit_id>
<unit_id>3</unit_id>
</stop>
<unit>
<id>1</id>
<count>2</count>
<value>1</value>
</unit>
<unit>
<id>2</id>
<count>4</count>
<value>1</value>
</unit>
<unit>
<id>3</id>
<count>2</count>
<value>3</value>
</unit>
The result i want to get is the one below
<test>
<stop>
<id>1</id>
<sum>6</sum>
</stop>
<stop>
<id>2</id>
<sum>10</sum>
</stop>
Any tips how to get it?
I tried with this example but the sum of the moltiplication doesn't work, it is ok for only the sum or the multiplication but not both
<xsl:template match="stop">
<xsl:variable name="ship_unit" select="id"/>
<xsl:value-of select="sum(following-sibling::unit[id=$ship_unit]/count*following-sibling::unit[id=$ship_unit]/value)"/>
If I am guessing correctly, you want to do something like:
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:key name="unit" match="unit" use="id" />
<xsl:template match="/test">
<xsl:copy>
<xsl:for-each select="stop">
<xsl:variable name="unit1" select="key('unit', unit_id[1])" />
<xsl:variable name="unit2" select="key('unit', unit_id[2])" />
<xsl:copy>
<xsl:copy-of select="id"/>
<sum>
<xsl:value-of select="$unit1/count * $unit1/value + $unit2/count * $unit2/value" />
</sum>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
However, the result of applying this to your input example will be:
<?xml version="1.0" encoding="utf-8"?>
<test>
<stop>
<id>1</id>
<sum>6</sum>
</stop>
<stop>
<id>2</id>
<sum>8</sum>
</stop>
</test>
and not what you posted.
I have the following input xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<results>
<case>
<KEY>c1</KEY>
<issue>
<KEY>i1</KEY>
<id>Apple</id>
</issue>
<issue>
<KEY>i1</KEY>
<id>Orange</id>
</issue>
<issue>
<KEY>i2</KEY>
<id>Mango</id>
</issue>
</case>
<case>
<KEY>c1</KEY>
<issue>
<KEY>i5</KEY>
<id>Apricot</id>
</issue>
<issue>
<KEY>i5</KEY>
<id>Blueberry</id>
</issue>
<issue>
<KEY>i6</KEY>
<id>blackberry</id>
</issue>
</case>
<case>
<KEY>c2</KEY>
<issue>
<KEY>i3</KEY>
<id>Banana</id>
</issue>
<issue>
<KEY>i3</KEY>
<id>Cherry</id>
</issue>
<issue>
<KEY>i4</KEY>
<id>Grapes</id>
</issue>
</case>
</results>
</root>
Now I want to group by <KEY> of <case> first and then by<KEY> of <issue>. The idea is to group all the <issue>'s by their issue key and case key. Finally I want to move all the <id>'s whose issue keys are same under <sourceInstance> node within the <issue>.
My output xml should be as below:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<results>
<case>
<KEY>c1</KEY>
<issue>
<KEY>i1</KEY>
<sources>
<sourceInstance>
<id>Apple</id>
</sourceInstance>
<sourceInstance>
<id>Orange</id>
</sourceInstance>
</sources>
</issue>
<issue>
<KEY>i2</KEY>
<sources>
<sourceInstance>
<id>Mango</id>
</sourceInstance>
</sources>
</issue>
<issue>
<KEY>i5</KEY>
<sources>
<sourceInstance>
<id>Apricot</id>
</sourceInstance>
<sourceInstance>
<id>Blueberry</id>
</sourceInstance>
</sources>
</issue>
<issue>
<KEY>i6</KEY>
<sources>
<sourceInstance>
<id>Apple</id>
</sourceInstance>
<sourceInstance>
<id>Orange</id>
</sourceInstance>
</sources>
</issue>
</case>
<case>
<KEY>c2</KEY>
<issue>
<KEY>i3</KEY>
<sources>
<sourceInstance>
<id>Banana</id>
</sourceInstance>
<sourceInstance>
<id>Cherry</id>
</sourceInstance>
</sources>
</issue>
<issue>
<KEY>i4</KEY>
<sources>
<sourceInstance>
<id>Grapes</id>
</sourceInstance>
</sources>
</issue>
</case>
</results>
</root>
I have tried by using the following XSLT, but couldn't get the desired xml output.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="case" match="case" use="string(KEY)" />
<xsl:key name="issue" match="case" use="concat(KEY, '|', KEY)" />
<xsl:template match="results">
<xsl:copy>
<xsl:apply-templates select="case[generate-id() = generate-id(key('case', string(KEY))[1])]" mode="case" />
</xsl:copy>
</xsl:template>
<xsl:template match="case" mode="case">
<xsl:choose>
<xsl:when test="KEY">
<case>
<xsl:copy-of select="KEY" />
<xsl:apply-templates select="key('case', KEY)[generate-id() = generate-id(key('issue', concat(KEY, '|', KEY))[1])]" mode="issue" />
</case>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="case " mode="issue">
<xsl:choose>
<xsl:when test="KEY">
<issue>
<xsl:copy-of select="KEY" />
<sources>
<xsl:apply-templates select="key('issue', id)" />
</sources>
</issue>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="case">
<sourceInstance>
<id>
<xsl:value-of select="id" />
</id>
</sourceInstance>
</xsl:template>
</xsl:stylesheet>
It appears that I am unable to frame the correct combination of a key using the <KEY> of <case> and <issue> as the node with the same name (i.e.<KEY> )exists at both the places.
Can somebody tell me what I am missing in my XSLT?
The main problem is the definition of your key of issue
<xsl:key name="issue" match="case" use="concat(KEY, '|', KEY)" />
You need to be matching issue elements here, and using a concatenation of the KEY for issue and the parent case element
<xsl:key name="issue" match="issue" use="concat(KEY, '|', ../KEY)" />
Then, to get the grouped issue elements for a given case KEY, you would do this...
<xsl:apply-templates select="key('case', KEY)/issue[generate-id() = generate-id(key('issue', concat(KEY, '|', ../KEY))[1])]" mode="issue" />
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="case" match="case" use="string(KEY)" />
<xsl:key name="issue" match="issue" use="concat(KEY, '|', ../KEY)" />
<xsl:template match="results">
<xsl:copy>
<xsl:apply-templates select="case[generate-id() = generate-id(key('case', string(KEY))[1])]" mode="case" />
</xsl:copy>
</xsl:template>
<xsl:template match="case" mode="case">
<xsl:choose>
<xsl:when test="KEY">
<case>
<xsl:copy-of select="KEY" />
<xsl:apply-templates select="key('case', KEY)/issue[generate-id() = generate-id(key('issue', concat(KEY, '|', ../KEY))[1])]" mode="issue" />
</case>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="issue" mode="issue">
<xsl:choose>
<xsl:when test="KEY">
<issue>
<xsl:copy-of select="KEY" />
<sources>
<xsl:apply-templates select="key('issue', concat(KEY, '|', ../KEY))" />
</sources>
</issue>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="issue">
<sourceInstance>
<id>
<xsl:value-of select="id" />
</id>
</sourceInstance>
</xsl:template>
</xsl:stylesheet>
I have an XML as below. For each test I need to sort cat and title and group .
For the cat value abc i need to get title and set values.
Any set values matches the need to add the title value inside the set node in ascending order. here i did hard coded the cat value.But in my scenario the cat value is getting from external source.
<?xml version="1.0" encoding="utf-8" ?>
<compound>
<test>
<title>k</title>
<cetval>
<cat>abc</cat>
<cat>fcg</cat>
</cetval>
<value>1</value>
<set>
<color>blue</color>
</set>
</test>
<test>
<title>p</title>
<cetval>
<cat>fcg</cat>
<cat>klm</cat>
</cetval>
<value>10</value>
<set>
<color>pink</color>
</set>
</test>
<test>
<title>j</title>
<cetval>
<cat>fcg</cat>
<cat>klm</cat>
<cat>abc</cat>
</cetval>
<value>484</value>
<set>
<color>yellow</color>
</set>
</test>
<test>
<title>v</title>
<cetval>
<cat>dji</cat>
<cat>kfjsdlk</cat>
</cetval>
<value>93</value>
</test>
<test>
<title>r</title>
<cetval>
<cat>deiu</cat>
<cat>kfjdf</cat>
<cat>abc</cat>
</cetval>
<value>10</value>
<set>
<color>blue</color>
</set>
<test>
<title>i</title>
<cetval>
<cat>fcg</cat>
<cat>klm</cat>
<cat>abc</cat>
</cetval>
<value>10</value>
</test>
<test>
<title>w</title>
<cetval>
<cat>diweif</cat>
<cat>fjf</cat>
<cat>abc</cat>
</cetval>
<value>10</value>
<set>
<color>yellow</color>
</set>
</test>
</test>
</compound>
I need the output as below:
<?xml version="1.0" encoding="utf-8" ?>
<compound>
<cat>abc</cat>
<set>
<color>blue</color>
<title>k</title>
<title>r</title>
</set>
<set>
<color>yellow</color>
<title>j</title>
<title>w</title>
</set>
<title>i</title>
</compound>
My transform looks like below:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="compound/test">
<xsl:sort select="./cetval/cat" order="ascending"/>
<xsl:sort select="./title" order="ascending"/>
<xsl:for-each select="./cetval/cat">
<xsl:choose>
<xsl:when test="./cat = 'abc' > 0">
<xsl:value-of select="ancestor::test/title"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Can any one suggest me how to generate the required output?
Ok, I think I've made sense of your requirements. How's this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:key name="kColor" match="test" use="set/color" />
<xsl:param name="catValue" select="'abc'" />
<xsl:template match="/*">
<xsl:copy>
<cat>
<xsl:value-of select ="$catValue"/>
</cat>
<xsl:apply-templates
select="//test[generate-id() =
generate-id(
key('kColor', set/color)
[cetval/cat = $catValue][1]
)
]" />
<xsl:apply-templates
select="//test[cetval/cat = $catValue and not(set/color)]
/title">
<xsl:sort select="."/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="test">
<set>
<xsl:apply-templates select="set/color" />
<xsl:apply-templates
select="key('kColor', set/color)[cetval/cat = $catValue]/title">
<xsl:sort select="." />
</xsl:apply-templates>
</set>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Here $catValue is specified as a parameter so you can pass a different value in from externally. When this is run on your sample XML, the result is:
<compound>
<cat>abc</cat>
<set>
<color>blue</color>
<title>k</title>
<title>r</title>
</set>
<set>
<color>yellow</color>
<title>j</title>
<title>w</title>
</set>
<title>i</title>
</compound>
Quite shorter and simpler:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kTestByCat" match="test" use="cetval/cat"/>
<xsl:key name="kTestByColor" match="test[cetval/cat='abc']"
use="set/color"/>
<xsl:template match="/">
<compound>
<cat>abc</cat>
<xsl:apply-templates select=
"/*/*[generate-id()
=
generate-id(key('kTestByColor',set/color)[1])]"/>
</compound>
</xsl:template>
<xsl:template match="test">
<set>
<color><xsl:value-of select="set/color"/></color>
<xsl:copy-of select="key('kTestByColor',set/color)/title"/>
</set>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<compound>
<test>
<title>k</title>
<cetval>
<cat>abc</cat>
<cat>fcg</cat>
</cetval>
<value>1</value>
<set>
<color>blue</color>
</set>
</test>
<test>
<title>p</title>
<cetval>
<cat>fcg</cat>
<cat>klm</cat>
</cetval>
<value>10</value>
<set>
<color>pink</color>
</set>
</test>
<test>
<title>j</title>
<cetval>
<cat>fcg</cat>
<cat>klm</cat>
<cat>abc</cat>
</cetval>
<value>484</value>
<set>
<color>yellow</color>
</set>
</test>
<test>
<title>v</title>
<cetval>
<cat>dji</cat>
<cat>kfjsdlk</cat>
</cetval>
<value>93</value>
</test>
<test>
<title>r</title>
<cetval>
<cat>deiu</cat>
<cat>kfjdf</cat>
<cat>abc</cat>
</cetval>
<value>10</value>
<set>
<color>blue</color>
</set>
<test>
<title>i</title>
<cetval>
<cat>fcg</cat>
<cat>klm</cat>
<cat>abc</cat>
</cetval>
<value>10</value>
</test>
<test>
<title>w</title>
<cetval>
<cat>diweif</cat>
<cat>fjf</cat>
<cat>abc</cat>
</cetval>
<value>10</value>
<set>
<color>yellow</color>
</set>
</test>
</test>
</compound>
the wanted, correct result is produced:
<compound>
<cat>abc</cat>
<set>
<color>blue</color>
<title>k</title>
<title>r</title>
</set>
<set>
<color>yellow</color>
<title>j</title>
<title>w</title>
</set>
</compound>
And if you want to produce similar results for all disting values of cat, it is again short and simple:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kCatByVal" match="cat" use="."/>
<xsl:key name="kColorByVal" match="color" use="."/>
<xsl:key name="kTestByCat" match="test" use="cetval/cat"/>
<xsl:variable name="vDistinctColors" select=
"/*/*/set/color[generate-id()=generate-id(key('kColorByVal',.)[1])]"/>
<xsl:template match="/">
<xsl:apply-templates select=
"/*/*/cetval/cat
[generate-id()
= generate-id(key('kCatByVal',.)[1])]"/>
</xsl:template>
<xsl:template match="cat">
<xsl:variable name="vcurCat" select="."/>
<compound>
<cat><xsl:apply-templates/></cat>
<xsl:for-each select="$vDistinctColors">
<xsl:apply-templates select=
"(key('kCatByVal',$vcurCat)/../../set/color[.=current()])[1]">
<xsl:with-param name="pCat" select="$vcurCat"/>
</xsl:apply-templates>
</xsl:for-each>
</compound>
</xsl:template>
<xsl:template match="color">
<xsl:param name="pCat"/>
<xsl:copy-of select="."/>
<xsl:copy-of select="key('kTestByCat',$pCat)[set/color=current()]/title"/>
</xsl:template>
</xsl:stylesheet>
When applied on the same XML document (above), the result now is:
<compound>
<cat>abc</cat>
<color>blue</color>
<title>k</title>
<title>r</title>
<color>yellow</color>
<title>j</title>
<title>w</title>
</compound>
<compound>
<cat>fcg</cat>
<color>blue</color>
<title>k</title>
<color>pink</color>
<title>p</title>
<color>yellow</color>
<title>j</title>
</compound>
<compound>
<cat>klm</cat>
<color>pink</color>
<title>p</title>
<color>yellow</color>
<title>j</title>
</compound>
<compound>
<cat>dji</cat>
</compound>
<compound>
<cat>kfjsdlk</cat>
</compound>
<compound>
<cat>deiu</cat>
<color>blue</color>
<title>r</title>
</compound>
<compound>
<cat>kfjdf</cat>
<color>blue</color>
<title>r</title>
</compound>
I am completely new to xslt. Please help me to write style sheet.I have input xml like this
Input XML:
<elements>
<e1>
<pid>1</pid>
<cid>2</cid>
</e1>
<e1>
<pid>1</pid>
<cid>3</cid>
</e1>
<e1>
<pid>2</pid>
<cid>4</cid>
</e1>
</elements>
Desired XML:
<tree>
<unit id="1">
<unit id="2">
<unit id="4">
<data></data>
</unit>
<data></data>
</unit>
<unit id="3">
<data></data>
</unit>
<data></data>
</unit>
</tree>
I feel this should be really easy but I'm struggling to find information about how to do this. My XSLT knowledge isn't great.
I'm not 100% sure how you want the XSLT to determine from that input that the top id is 1 (is it because it's the only pid value with no corresponding cid values, or is it always 1?). Nonetheless, this should do the job:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="kItemsByC" match="e1" use="cid" />
<xsl:key name="kItemsByP" match="e1" use="pid" />
<xsl:template match="/">
<tree>
<xsl:call-template name="Unit">
<!-- This will be the value of the <pid> that has no <cid> references to
it (assuming there is only one top-level <pid>) -->
<xsl:with-param name="id"
select="string(/elements/e1/pid[not(key('kItemsByC', .))])" />
</xsl:call-template>
</tree>
</xsl:template>
<xsl:template match="e1" name="Unit">
<xsl:param name="id" select="cid" />
<unit id="{$id}">
<xsl:apply-templates select="key('kItemsByP', $id)" />
<data />
</unit>
</xsl:template>
</xsl:stylesheet>
When this is run on your sample input, this produces:
<tree>
<unit id="1">
<unit id="2">
<unit id="4">
<data />
</unit>
<data />
</unit>
<unit id="3">
<data />
</unit>
<data />
</unit>
</tree>
Note: The above XSLT has logic to attempt to dynamically locate the top-level ID. If it can be assumed that the top-level unit will always have ID 1, then one key and the above XSLT's (somewhat) complicated formula can be eliminated:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="kItemsByP" match="e1" use="pid" />
<xsl:template match="/">
<tree>
<xsl:call-template name="Unit">
<xsl:with-param name="id" select="1" />
</xsl:call-template>
</tree>
</xsl:template>
<xsl:template match="e1" name="Unit">
<xsl:param name="id" select="cid" />
<unit id="{$id}">
<xsl:apply-templates select="key('kItemsByP', $id)" />
<data />
</unit>
</xsl:template>
</xsl:stylesheet>
This also produces the requested output when run on your sample input.
Ah, after reading JLRishe I think I get it: "pid" means "parent ID", "cid" means "child ID", and e1 represents a parent-child relationship. Brilliant detective work, I would never have worked that out for myself.
The basic model is that when you are positioned on a parent element you do apply-templates to its children. This applies just as well if the parent/child relationships are represented by primary/foreign keys as when they are represented using the XML hierarchy. So the essence is:
<xsl:template match="e1">
<unit id="{pid}">
<xsl:apply-templates select="//e1[pid=current()/cid]"/>
<data/>
</unit>
</xsl:template>
which is essentially JLRishe's solution except he has added an optimization using keys.