I want to find distinct values across each node. but when i use muenchian method it will select distinct values only at top level.
Currently i am getting output as:
Bag 20
Tray 30
But i want to group type and quantity for each item
<!--for 1st item-->
Bag 20
Tray 30
<!--for 2nd item-->
Bag 20
Box 20
Tray 30
I am doing some research to make it work but did not succeed.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<Test>
<Items>
<Item>
<name>A</name>
<type>Bag</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Bag</type>
<input quantity="20"/>
</Item>
<Item>
<name>B</name>
<type>Metal</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Tray</type>
<input quantity="30"/>
</Item>
</Items>
<Items>
<Item>
<name>A</name>
<type>Bag</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Box</type>
<input quantity="20"/>
</Item>
<Item>
<name>B</name>
<type>Metal</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Tray</type>
<input quantity="30"/>
</Item>
</Items>
</Test>
Code:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:date="http://exslt.org/dates-and-times"
xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl"
extension-element-prefixes="date"
>
<xsl:key name="item-key" match="Items/Item[name='A']" use="input/#quantity" />
<xsl:template match="/" name="Barcode">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="ManufacturLabelSize-first" page-height="297mm" page-width="210mm" margin-top="25.4mm" margin-right="25.4mm" margin-left="25.4mm" margin-bottom="25.4mm">
<fo:region-body margin-top="15mm" />
<fo:region-before />
<fo:region-after />
</fo:simple-page-master>
<fo:simple-page-master master-name="ManufacturLabelSize-rest" page-height="297mm" page-width="210mm" margin-top="25.4mm" margin-right="25.4mm" margin-left="25.4mm" margin-bottom="25.4mm">
<fo:region-body margin-top="15mm"/>
<fo:region-before />
<fo:region-after />
</fo:simple-page-master>
<fo:page-sequence-master master-name="ManufacturLabelSize-portrait">
<fo:repeatable-page-master-alternatives>
<fo:conditional-page-master-reference master-reference="ManufacturLabelSize-first"
page-position="first"/>
<fo:conditional-page-master-reference master-reference="ManufacturLabelSize-rest"
page-position="rest"/>
</fo:repeatable-page-master-alternatives>
</fo:page-sequence-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="ManufacturLabelSize-portrait" id="pSeqID">
<fo:flow flow-name="xsl-region-body">
<fo:table >
<fo:table-body border="solid" border-width="0.5pt">
<fo:table-row>
<fo:table-cell>
<fo:block>
<xsl:for-each select="Test">
<xsl:for-each select="Items">
<xsl:for-each select="Item[name='A'][count(. | key('item-key',input/#quantity)[1])=1]">
<fo:block>
<xsl:value-of select="type"/> <fo:inline color="white">XXX</fo:inline><xsl:value-of select="input/#quantity"/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
Appreciate your help!
If - as it seems - you want to find distinct Items whose name is A by their type separately for each Items, then you need to include the parent Items' id in the key. Here's a simplified example:
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="k" match="Item[name='A']" use="concat(type, '|', generate-id(..))"/>
<xsl:template match="/Test">
<root>
<xsl:for-each select="Items">
<xsl:comment>
<xsl:value-of select="position()" />
</xsl:comment>
<xsl:for-each select="Item[name='A'][count(. | key('k', concat(type, '|', generate-id(..)))[1]) = 1]">
<item>
<type>
<xsl:value-of select="type"/>
</type>
<quantity>
<xsl:value-of select="input/#quantity"/>
</quantity>
</item>
</xsl:for-each>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, the result will be:
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<!--1-->
<item>
<type>Bag</type>
<quantity>20</quantity>
</item>
<item>
<type>Tray</type>
<quantity>30</quantity>
</item>
<!--2-->
<item>
<type>Bag</type>
<quantity>20</quantity>
</item>
<item>
<type>Box</type>
<quantity>20</quantity>
</item>
<item>
<type>Tray</type>
<quantity>30</quantity>
</item>
</root>
Related
I have done few calculations using key-match for each line and i am expecting to sum the total of these values at the last row.
Below is the calculation.
LINE#1
4 (DispatchQuantity = 2; occurrence = 2 -> 2 x 2 = 4) MID-LAYER 12 (UnitNetWeight = 3KG x 12)
4 (DispatchQuantity = 2; occurrence = 2 -> 2 x 2 = 4) PALLET-LAYER 12 (UnitNetWeight = 3KG x 12)
LINE#2
8 (DispatchQuantity = 4; occurrence = 2 -> 4 x 2 = 8) MID-LAYER 24 (UnitNetWeight = 3KG x 8)
I need to add the numbers of each line to produce one last block which should contains 16 (4+4+8) and 48(12+12+24)
current o/p:
4 MID-LAYER 12
4 PALLET-LAYER 12
8 MID-LAYER 24
XML:
<?xml version="1.0" encoding="UTF-8"?>
<cXML payloadID="16314uu19eii6-765h52u1pp0.162.97.178" timestamp="2021-10-06T02:54:24-07:00" version="1.2.051" xml:lang="en-PL">
<Header>
<Line>
<Items>
<Item>
<Dimension type="unitNetWeight" quantity="3.0"/>
<Code>One</Code>
<CodeIdentifier>MID-LAYER</CodeIdentifier>
<DispatchQuantity quantity="2.0"/>
</Item>
<Item>
<Dimension type="unitNetWeight" quantity="3.0"/>
<Code>auxiliary</Code>
<CodeIdentifier>MID-LAYER</CodeIdentifier>
<DispatchQuantity quantity="2.0"/>
</Item>
<Item>
<Dimension type="unitNetWeight" quantity="3.0"/>
<Code>auxiliary</Code>
<CodeIdentifier>MID-LAYER</CodeIdentifier>
<DispatchQuantity quantity="2.0"/>
</Item>
<Item>
<Dimension type="unitNetWeight" quantity="3.0"/>
<Code>auxiliary</Code>
<CodeIdentifier>PALLET-LAYER</CodeIdentifier>
<DispatchQuantity quantity="2.0"/>
</Item>
<Item>
<Dimension type="unitNetWeight" quantity="3.0"/>
<Code>auxiliary</Code>
<CodeIdentifier>PALLET-LAYER</CodeIdentifier>
<DispatchQuantity quantity="2.0"/>
</Item>
</Items>
<Items>
<Item>
<Dimension type="unitNetWeight" quantity="3.0"/>
<Code>auxiliary</Code>
<CodeIdentifier>MID-LAYER</CodeIdentifier>
<DispatchQuantity quantity="4.0"/>
</Item>
<Item>
<Dimension type="unitNetWeight" quantity="3.0"/>
<Code>auxiliary</Code>
<CodeIdentifier>MID-LAYER</CodeIdentifier>
<DispatchQuantity quantity="4.0"/>
</Item>
</Items>
</Line>
</Header>
</cXML>
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:date="http://exslt.org/dates-and-times"
extension-element-prefixes="date"
>
<!--unique key-->
<xsl:key name="aux" match="Item[Code='auxiliary']" use="concat(CodeIdentifier, '|', generate-id(..))"/>
<xsl:decimal-format name="generalFormat" grouping-separator="," decimal-separator="." />
<xsl:output method="xml" indent="yes" />
<xsl:template match="/" name="Barcode">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="ManufacturLabelSize-first" page-height="297mm" page-width="210mm" margin-top="25.4mm" margin-right="25.4mm" margin-left="25.4mm" margin-bottom="25.4mm">
<fo:region-body margin-top="15mm" />
<fo:region-before />
<fo:region-after />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="ManufacturLabelSize-first" id="pSeqID">
<fo:flow flow-name="xsl-region-body">
<fo:table>
<fo:table-body border="solid" border-width="0.5pt">
<xsl:for-each select="cXML/Header/Line">
<xsl:for-each select="Items">
<fo:table-row>
<fo:table-cell>
<!--DispatchQuantity*occurence-->
<fo:block>
<xsl:for-each select="Item[Code='auxiliary'][count(. | key('aux', concat(CodeIdentifier, '|', generate-id(..)))[1]) = 1]">
<fo:block >
<xsl:value-of select="(DispatchQuantity/#quantity) * count(key('aux', concat(CodeIdentifier, '|', generate-id(..))))"/>
</fo:block>
</xsl:for-each>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<!--unique code-->
<fo:block>
<xsl:for-each select="Item[Code='auxiliary'][count(. | key('aux', concat(CodeIdentifier, '|', generate-id(..)))[1]) = 1]">
<fo:block >
<xsl:value-of select="CodeIdentifier"/>
</fo:block>
</xsl:for-each>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<!--(DispatchQuantity*occurence)*UnitNetWeight-->
<fo:block>
<xsl:for-each select="Item[Code='auxiliary'][count(. | key('aux', concat(CodeIdentifier, '|', generate-id(..)))[1]) = 1]">
<fo:block font-size="8" font-family="Calibri" padding-before="2mm" start-indent="0.75mm">
<xsl:value-of select="(DispatchQuantity/#quantity) * count(key('aux', concat(CodeIdentifier, '|', generate-id(..)))) * Dimension[#type='unitNetWeight']/#quantity"/>
</fo:block>
</xsl:for-each>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
Your kind help is much valued!
Consider this simplified example:
XSLT 1.0 (+EXSLT node-set() function)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:key name="aux" match="Item[Code='auxiliary']" use="concat(CodeIdentifier, '|', generate-id(..))"/>
<xsl:template match="cXML">
<xsl:variable name="groups-RTF">
<xsl:for-each select="//Item[Code='auxiliary'][count(. | key('aux', concat(CodeIdentifier, '|', generate-id(..)))[1]) = 1]">
<group code="{CodeIdentifier}">
<xsl:variable name="group-size" select="count(key('aux', concat(CodeIdentifier, '|', generate-id(..))))"/>
<xsl:variable name="quantity" select="DispatchQuantity/#quantity * $group-size"/>
<quantity>
<xsl:value-of select="$quantity"/>
</quantity>
<weight>
<xsl:value-of select="Dimension[#type='unitNetWeight']/#quantity * $quantity"/>
</weight>
</group>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="groups" select="exsl:node-set($groups-RTF)/group" />
<!-- output -->
<xsl:text>code, quantity, weight
</xsl:text>
<xsl:for-each select="$groups">
<xsl:value-of select="#code"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="quantity"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="weight"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:text>TOTAL, </xsl:text>
<xsl:value-of select="sum($groups/quantity)"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="sum($groups/weight)"/>
</xsl:template>
</xsl:stylesheet>
Result
code, quantity, weight
MID-LAYER, 4, 12
PALLET-LAYER, 4, 12
MID-LAYER, 8, 24
TOTAL, 16, 48
I have a xml with multiple items, which are Multilevel BOM (in the example 2 items, with both three levels). I need this to convert to xml with each record only the father and the Childs (first record of each item has no father).
We use XSLT 1.0 and we can't use Muenchian grouping because the processor in use don't know the key function.
I hope someone can help me out.
XML example:
<Items>
<level01>
<itemcode>L100</itemcode>
<quantity>1</quantity>
<whs>30</whs>
<level02>
<row>
<itemcode>L201</itemcode>
<quantity>5</quantity>
<whs>02</whs>
</row>
<row>
<itemcode>L202</itemcode>
<quantity>8</quantity>
<whs>01</whs>
</row>
<row>
<itemcode>L203</itemcode>
<quantity>1</quantity>
<whs>01</whs>
<level03>
<row>
<itemcode>L301</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</row>
<row>
<itemcode>L302</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</row>
</level03>
</row>
</level02>
</level01>
<level01>
<itemcode>M100</itemcode>
<quantity>1</quantity>
<whs>30</whs>
<level02>
<row>
<itemcode>M201</itemcode>
<quantity>3</quantity>
<whs>01</whs>
</row>
<row>
<itemcode>M202</itemcode>
<quantity>2</quantity>
<whs>01</whs>
</row>
<row>
<itemcode>M203</itemcode>
<quantity>2</quantity>
<whs>01</whs>
<level03>
<row>
<itemcode>M301</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</row>
<row>
<itemcode>M302</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</row>
</level03>
</row>
</level02>
</level01>
</Items>
desired result:
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Item>
<itemcode>L100</itemcode>
<quantity>1</quantity>
<whs>02</whs>
</Item>
<Item>
<father>L100</father>
<itemcode>L201</itemcode>
<quantity>5</quantity>
<whs>02</whs>
</Item>
<item>
<father>L100</father>
<itemcode>L202</itemcode>
<quantity>8</quantity>
<whs>01</whs>
</item>
<item>
<father>L100</father>
<itemcode>L203</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</item>
<item>
<father>L203</father>
<itemcode>L301</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</item>
<item>
<father>L203</father>
<itemcode>L302</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</item>
</Items>
<Items>
<item>
<itemcode>M100</itemcode>
<quantity>1</quantity>
<whs>02</whs>
</item>
<item>
<father>M100</father>
<itemcode>M201</itemcode>
<quantity>3</quantity>
<whs>01</whs>
</item>
<item>
<father>M100</father>
<itemcode>M202</itemcode>
<quantity>2</quantity>
<whs>01</whs>
</item>
<item>
<father>M100</father>
<itemcode>M203</itemcode>
<quantity>2</quantity>
<whs>01</whs>
</item>
<item>
<father>M203</father>
<itemcode>M301</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</item>
<item>
<father>M203</father>
<itemcode>M302</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</item>
</items>
<?bpc.pltype.out bpm.pltype=xml?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:b1e="urn:com.sap.b1i.sim:b1event" xmlns:b1ie="urn:com.sap.b1i.sim:b1ievent" xmlns:b1im="urn:com.sap.b1i.sim:b1imessage" xmlns:bfa="urn:com.sap.b1i.bizprocessor:bizatoms" xmlns:exslt="http://exslt.org/common"
xmlns:jdbc="urn:com.sap.b1i.adapter:jdbcadapter" xmlns:js="com.sap.b1i.bpc_tools.Javascript" xmlns:rev="urn:com.sap.b1i.adapter:revaadapter" xmlns:rfc="urn:sap-com:document:sap:rfc:functions" xmlns:sim="urn:com.sap.b1i.sim:entity" xmlns:utils="com.sap.b1i.bpc_tools.Utilities"
xmlns:vpf="urn:com.sap.b1i.vplatform:entity" xmlns:xci="urn:com.sap.b1i.xcellerator:intdoc" version="1.0" exclude-result-prefixes="b1e b1ie b1im bfa jdbc js rfc utils xci vpf exslt sim rev" b1e:force="" b1ie:force="" b1im:force="" bfa:force="" jdbc:force=""
js:force="" rfc:force="" utils:force="" xci:force="" vpf:force="" exslt:force="" sim:force="" rev:force="">
<?prodver 1.0.0?>
<!--<xsl:include href="../../com.sap.b1i.dev.repository/IDE/init.xsl" />-->
<xsl:variable name="msg" select="/vpf:Msg/vpf:Body/vpf:Payload[./#Role='S']" />
<xsl:template match="/">
<Msg xmlns="urn:com.sap.b1i.vplatform:entity">
<xsl:copy-of select="/vpf:Msg/#*" />
<xsl:copy-of select="/vpf:Msg/vpf:Header" />
<Body>
<xsl:copy-of select="/vpf:Msg/vpf:Body/*" />
<Payload Role="X" id="999999">
<xsl:call-template name="transform" />
</Payload>
</Body>
</Msg>
</xsl:template>
<xsl:template name="transform">
This is the space we usually add our code
</xsl:template>
</xsl:stylesheet>
As I mentioned in the comments, I see no need for grouping here. The only complication is the irregularity of your input (no row wrapper at the top level) and of the output (no father element for the items with no parent). Otherwise this could be even simpler.
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="/Items">
<xsl:copy>
<xsl:apply-templates select="level01"/>
</xsl:copy>
</xsl:template>
<xsl:template match="level01">
<Item>
<xsl:copy-of select="itemcode|quantity|whs"/>
</Item>
<xsl:apply-templates select="*/row"/>
</xsl:template>
<xsl:template match="row">
<Item>
<father>
<xsl:value-of select="(ancestor::*/itemcode)[last()]"/>
</father>
<xsl:copy-of select="itemcode|quantity|whs"/>
</Item>
<xsl:apply-templates select="*/row"/>
</xsl:template>
</xsl:stylesheet>
Do note that the output is somewhat different from the one in your question: the Items element is the root element of the entire output tree. Without this, you would receive an XML fragment instead of a well-formed XML document.
If you want an additional wrapper for each main branch, change the template matching level01 to:
<xsl:template match="level01">
<Branch>
<Item>
<xsl:copy-of select="itemcode|quantity|whs"/>
</Item>
<xsl:apply-templates select="*/row"/>
</Branch>
</xsl:template>
How to rename the parent node and rearrange the child nodes simultaneously.
I tried some code but was unable to get the desired output.
This was the output that i was getting
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header> </Header>
<Body>
<MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Run xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Run">
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<A3>02</A3>
<A4>ER</A4>
</RunObject>
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<A3>03</A3>
<A4>ER</A4>
</RunObject>
</Run>
</MessageParts>
</Body>
</Envelope>
I wanted to remove the namespaces, rename the Parent tags and rearrange the child tags.
So i used this code
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run"
exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<xsl:template match="r:RunObject[#class='entity']">
<xsl:copy>
<xsl:apply-templates select="A3" />
<xsl:apply-templates select="A4" />
<xsl:apply-templates select="A2" />
<xsl:apply-templates select="A1" />
</xsl:copy>
</xsl:template>
<!-- rename RunObject to Item -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates />
</Item>
</xsl:template>
</xsl:stylesheet>
I was able to remove the namespaces, rename the parent tags but was unable to rearrange the child tags. The output i got was this
?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header> </Header>
<Body>
<Document>
<Item>
<A1>NA</A1>
<A2>False</A2>
<A3>02</A3>
<A4>ER</A4>
</Item>
<Item>
<A1>NA</A1>
<A2>False</A2>
<A3>03</A3>
<A4>ER</A4>
</Item>
</Document>
</Body>
</Envelope>
But the desired output is this
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header> </Header>
<Body>
<Document>
<Item>
<A3>02</A3>
<A4>ER</A4>
<A2>False</A2>
<A1>NA</A1>
</Item>
<Item>
<A3>03</A3>
<A4>ER</A4>
<A2>False</A2>
<A1>NA</A1>
</Item>
</Document>
</Body>
</Envelope>
Problem #1:
You have two templates matching the same nodes:
<xsl:template match="r:RunObject[#class='entity']">
Only the last one of these will be applied.
Problem #2:
The elements A1, A2, A3 and A4 are in a namespace inherited from their Run ancestor. You need to use the prefix bound to that namespace when selecting them.
See if this works for you:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run"
exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<!-- rename RunObject to Item + reorder child nodes -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates select="r:A3" />
<xsl:apply-templates select="r:A4" />
<xsl:apply-templates select="r:A2" />
<xsl:apply-templates select="r:A1" />
</Item>
</xsl:template>
</xsl:stylesheet>
Today's challenge was grouping in XSLT 1.0. Found out there are something called keys and the Muenchian grouping.
Input XML:
<Items>
<Item>
<ID>1</ID>
<Name>A</Name>
<Country>Sweden</Country>
<Region>Småland</Region>
</Item>
<Item>
<ID>2</ID>
<Name>B</Name>
<Country>Sweden</Country>
<Region>Norrland</Region>
</Item>
<Item>
<ID>3</ID>
<Name>C</Name>
<Country>USA</Country>
<Region>Alaska</Region>
</Item>
<Item>
<ID>4</ID>
<Name>D</Name>
<Country>USA</Country>
<Region>Texas</Region>
</Item>
<Item>
<ID>5</ID>
<Name>E</Name>
<Country>Sweden</Country>
<Region>Norrland</Region>
</Item>
</Items>
I need to make thins XML into a better structure, and from this sample XML I't like to get items structured by country and region. Below is wanted result where country and region gets sorted as well:
<Items>
<Country Name="Sweden">
<Region Name="Norrland">
<Item>
<ID>2</ID>
<Name>B</Name>
</Item>
<Item>
<ID>5</ID>
<Name>E</Name>
</Item>
</Region>
<Region Name="Småland">
<Item>
<ID>1</ID>
<Name>A</Name>
</Item>
</Region>
</Country>
<Country Name="USA">
<Region Name="Alaska">
<Item>
<ID>3</ID>
<Name>C</Name>
</Item>
</Region>
<Region Name="Texas">
<Item>
<ID>4</ID>
<Name>D</Name>
</Item>
</Region>
</Country>
</Items>
EDIT:
I also want to make sure regions end up in their own country, even if there are duplicates. I edited the answer accordingly.
Also, I'd like to hint about xsltfiddle.liberty-development.net as an easy way of doing trial-and-error XSLT development...
Inspired by this article, I found a neat solution to this problem:
I have included comments for using it for single or double grouping, see comments in the code. Notice how I use first key (index) as input to the secon for-each loop:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="country" match="Item" use="Country" />
<xsl:key name="region" match="Item" use="concat(Region, '|', Country)" />
<xsl:template match="/Items">
<Items>
<xsl:for-each select="Item[generate-id(.) = generate-id(key('country', Country))]">
<xsl:sort select="Country" />
<xsl:variable name="_country" select="Country" />
<xsl:element name="Country">
<xsl:attribute name="Name"><xsl:value-of select="$_country" /></xsl:attribute>
<!-- single level grouping -->
<!--<xsl:apply-templates select="key('country', Country)" />-->
<!-- double grouping -->
<!-- START -->
<xsl:for-each select="key('country', Country)[generate-id(.) = generate-id(key('region', concat(Region, '|', Country)))]">
<xsl:sort select="Region" />
<xsl:variable name="_region" select="Region" />
<xsl:element name="Region">
<xsl:attribute name="Name"><xsl:value-of select="$_region" /></xsl:attribute>
<xsl:apply-templates select="key('region', concat(Region, '|', Country))" />
</xsl:element>
</xsl:for-each>
<!-- END -->
</xsl:element>
</xsl:for-each>
</Items>
</xsl:template>
<xsl:template match="Item">
<xsl:element name="Item">
<xsl:element name="ID"><xsl:value-of select="ID" /></xsl:element>
<xsl:element name="Name"><xsl:value-of select="Name" /></xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I need to convert a name value pair into XML. I'm able to generate an XML, but the element name should be grouped and it should not be duplicated. Please see below. The FieldValue element contains 2 OrderItem values in the Detail node. If the FieldValue with OrderItem repeats, then the result should be grouped into one OrderItem node. Please help.
Source XML:
<SC>
<Header>
<Record>
<FieldName>Schema</FieldName>
<FieldValue>OrderHeader</FieldValue>
</Record>
<Record>
<FieldName>Order</FieldName>
<FieldValue>1234</FieldValue>
</Record>
</Header>
<Detail>
<Record>
<FieldName>Schema</FieldName>
<FieldValue>OrderItem</FieldValue>
</Record>
<Record>
<FieldName>Item</FieldName>
<FieldValue>1</FieldValue>
</Record>
<Record>
<FieldName>Qty</FieldName>
<FieldValue>10</FieldValue>
</Record>
</Detail>
<Detail>
<Record>
<FieldName>Schema</FieldName>
<FieldValue>OrderItem</FieldValue>
</Record>
<Record>
<FieldName>Item</FieldName>
<FieldValue>2</FieldValue>
</Record>
<Record>
<FieldName>Qty</FieldName>
<FieldValue>20</FieldValue>
</Record>
</Detail>
</SC>
Target XML:
<Order>
<OrderItem>
<Item>
<Item>1</Item>
<Qty>10</Qty>
</Item>
<Item>
<Item>2</Item>
<Qty>20</Qty>
</Item>
</OrderItem>
</Order>
XSLT:
<xsl:template match="#*|node()">
<Order>
<xsl:for-each select="Detail">
<Item>
<xsl:apply-templates select="Record[position()>1]"/>
</Item>
</xsl:for-each>
</Order>
</xsl:template>
<xsl:template match="Record">
<xsl:element name="{FieldName}">
<xsl:value-of select="FieldValue"/>
</xsl:element>
</xsl:template>
The grouping can be done as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="SC">
<Order>
<xsl:for-each-group select="Detail" group-by="Record[1]/FieldValue">
<xsl:element name="{current-grouping-key()}">
<xsl:apply-templates select="current-group()"/>
</xsl:element>
</xsl:for-each-group>
</Order>
</xsl:template>
<xsl:template match="Detail">
<Item>
<xsl:apply-templates select="Record[position() gt 1]"/>
</Item>
</xsl:template>
<xsl:template match="Record">
<xsl:element name="{FieldName}">
<xsl:value-of select="FieldValue"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It appears that you are trying to define two XSLT templates, when one should be sufficient. You want to match on the root and then that you want to iterate over each SC/Detail.
Then, you want to take the FieldValue of the sibling of the FieldName node that is 'Item' (for item value) and 'Qty' (for quantity value), but only those listed under 'Record'.
Note: You have specified a doubly-nested <Item> in your transformed output and this solution reflects that requirement.
This XSLT should do what you are requesting:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:for-each select="SC/Detail">
<Order>
<OrderItem>
<Item>
<Item>
<xsl:value-of select="Record[FieldName[text()='Item']]/FieldValue" />
</Item>
<Qty>
<xsl:value-of select="Record[FieldName[text()='Qty']]/FieldValue" />
</Qty>
</Item>
</OrderItem>
</Order>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>