XSLT fails when envelop is added - xslt

I have a perfectly working XSL (thanks to you / StackOverflow):
https://xsltfiddle.liberty-development.net/3NgtZRc
Input xml
<Containers xmlns="https://www.uniconcreation.com/2021/IvenzaShippingContainer">
<Container>
<Name>INTERIEUR1</Name>
<Number>1</Number>
<SSCC>111</SSCC>
<SubContainers>
<Container>
<Id>I1371851</Id>
<SalesOrderNumber>2012231</SalesOrderNumber>
<ProductionOrderNumber>I2017658</ProductionOrderNumber>
<Name>ACCESSOIRE1</Name>
<Barcode>181001371851</Barcode>
<Items>
<Item>
<Id>I8709475</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
<Item>
<Id>I8709476</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
<Container>
<Id>I1371852</Id>
<SalesOrderNumber>2012231</SalesOrderNumber>
<ProductionOrderNumber>I2017658</ProductionOrderNumber>
<Name>PANEEL1</Name>
<Barcode>181001371852</Barcode>
<Items>
<Item>
<Id>I8709492</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
</SubContainers>
</Container>
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xpath-default-namespace="https://www.uniconcreation.com/2021/IvenzaShippingContainer" xmlns="https://www.uniconcreation.com/2021/IvenzaShippingContainer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="Containers">
<xsl:variable name="sscc" select="Container/SSCC"/>
<xsl:copy>
<xsl:for-each select="//Container">
<xsl:variable name="level" select="count(ancestor::*)"/>
<xsl:choose>
<xsl:when test="$level = 1">
<xsl:copy>
<xsl:copy-of select="Id"/>
<xsl:copy-of select="SalesOrderNumber"/>
<xsl:copy-of select="ProductionOrderNumber"/>
<xsl:copy-of select="Name"/>
<xsl:copy-of select="Type"/>
<xsl:copy-of select="Number"/>
<xsl:copy-of select="SSCC"/>
<xsl:copy-of select="Barcode"/>
<xsl:copy-of select="StartedAt"/>
<xsl:copy-of select="CompletedAt"/>
<xsl:choose>
<xsl:when test="SubContainers">
<Items>
<xsl:for-each select="SubContainers">
<Item>
<Id>
<xsl:value-of select="Container[1]/Id"/>
</Id>
<SalesOrderRowId>
<xsl:value-of select="Container[1]/Items/Item[1]/SalesOrderRowId"/>
</SalesOrderRowId>
<Quantity>1</Quantity>
</Item>
</xsl:for-each>
</Items>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="Items"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<Containers xmlns="https://www.uniconcreation.com/2021/IvenzaShippingContainer">
<Container>
<Name>INTERIEUR1</Name>
<Number>1</Number>
<SSCC>111</SSCC>
<Items>
<Item>
<Id>I1371851</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
</Containers>
*Especially the xpath-default-namespace helped me preventing getting an empty xmlns="" for the Items Literal
But then I found out SAP PI puts an envelop around my message:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Messages xmlns:ns0="http://sap.com/xi/XI/SplitAndMerge">
<ns0:Message1>
<Containers xmlns="https://www.uniconcreation.com/2021/IvenzaShippingContainer">
<Container>
<Name>INTERIEUR1</Name>
<Number>1</Number>
<SSCC>111</SSCC>
<SubContainers>
<Container>
<Id>I1371851</Id>
<SalesOrderNumber>2012231</SalesOrderNumber>
<ProductionOrderNumber>I2017658</ProductionOrderNumber>
<Name>ACCESSOIRE1</Name>
<Barcode>181001371851</Barcode>
<Items>
<Item>
<Id>I8709475</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
<Item>
<Id>I8709476</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
<Container>
<Id>I1371852</Id>
<SalesOrderNumber>2012231</SalesOrderNumber>
<ProductionOrderNumber>I2017658</ProductionOrderNumber>
<Name>PANEEL1</Name>
<Barcode>181001371852</Barcode>
<Items>
<Item>
<Id>I8709492</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
</SubContainers>
</Container>
</Containers>
</ns0:Message1>
</ns0:Messages>
And I ended up with this:
<?xml version="1.0" encoding="UTF-8"?>
<Containers xmlns="https://www.uniconcreation.com/2021/IvenzaShippingContainer"
xmlns:ns0="http://sap.com/xi/XI/SplitAndMerge"/>
I tried prefixing all namespaces but I hope there's another way :(
Required output
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Messages xmlns:ns0="http://sap.com/xi/XI/SplitAndMerge">
<ns0:Message1>
<Containers xmlns="https://www.uniconcreation.com/2021/IvenzaShippingContainer">
<Container>
<Name>INTERIEUR1</Name>
<Number>1</Number>
<SSCC>111</SSCC>
<Items>
<Item>
<Id>I1371851</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
</Containers>
</ns0:Message1>
</ns0:Messages>
Kind regards,
Mike

With two wrapper elements, the value of <xsl:variable name="level" select="count(ancestor::*)"/> is going to change so perhaps your check <xsl:when test="$level = 1"> needs to be converted to <xsl:when test="$level = 3">.
You will also need to add some identity template to make sure the wrapper elements are copied.

Related

Restructure xml with recursive nodes

I have an issue in SAP PI with an xml with recursive nodes. I have a Container which can have a SubContainer with (another) Container.
Input xml
<Containers xmlns="https://www.uniconcreation.com/2021/IvenzaShippingContainer">
<Container>
<Name>INTERIEUR1</Name>
<Number>1</Number>
<SSCC>111</SSCC>
<SubContainers>
<Container>
<Id>I1371851</Id>
<SalesOrderNumber>2012231</SalesOrderNumber>
<ProductionOrderNumber>I2017658</ProductionOrderNumber>
<Name>ACCESSOIRE1</Name>
<Barcode>181001371851</Barcode>
<Items>
<Item>
<Id>I8709475</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
<Item>
<Id>I8709476</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
<Container>
<Id>I1371852</Id>
<SalesOrderNumber>2012231</SalesOrderNumber>
<ProductionOrderNumber>I2017658</ProductionOrderNumber>
<Name>PANEEL1</Name>
<Barcode>181001371852</Barcode>
<Items>
<Item>
<Id>I8709492</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
</SubContainers>
</Container>
Challenge: I want to move the child Container nodes of the SubContainers to the root level and use the value of SSCC in these nodes. Thus getting rid of the SubContainers element.
Required result
<Containers xmlns="https://www.uniconcreation.com/2021/IvenzaShippingContainer">
<Container>
<Id>I1371851</Id>
<SalesOrderNumber>2012231</SalesOrderNumber>
<ProductionOrderNumber>I2017658</ProductionOrderNumber>
<Name>ACCESSOIRE1</Name>
<SSCC>111</SSCC>
<Barcode>181001371851</Barcode>
<Items>
<Item>
<Id>I8709475</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
<Item>
<Id>I8709476</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
<Container>
<Id>I1371852</Id>
<SalesOrderNumber>2012231</SalesOrderNumber>
<ProductionOrderNumber>I2017658</ProductionOrderNumber>
<Name>PANEEL1</Name>
<SSCC>111</SSCC>
<Barcode>181001371852</Barcode>
<Items>
<Item>
<Id>I8709492</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
</Container>
</Containers>
</Containers>
My current xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="https://www.uniconcreation.com/2021/IvenzaShippingContainer" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ext:Name|ext:Number|ext:SSCC|#*" />
<xsl:template match="ext:Containers/ext:Container/ext:SubContainers[ext:Container]">
<xsl:variable name="sscc" select="/ext:Containers/ext:Container/ext:SSCC"/>
<xsl:for-each select="*">
<xsl:element name="SSCC" namespace="{namespace-uri()}">
<xsl:value-of select="$sscc" /></xsl:element>
<xsl:copy-of select="node()"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I'm not doing a real great job if you look at my result on xsltfiddle :(
I'm stuck with the original parent and I don't manage to get the 2 elements around my child nodes.
Kind regards,
Mike D
If I am guessing correctly, all you need to do is simply:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="https://www.uniconcreation.com/2021/IvenzaShippingContainer">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Containers">
<xsl:variable name="sscc" select="Container/SSCC"/>
<xsl:copy>
<xsl:for-each select="//SubContainers/Container">
<xsl:copy>
<xsl:copy-of select="* except (Barcode, Items)"/>
<xsl:copy-of select="$sscc"/>
<xsl:copy-of select="Barcode, Items"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<Containers xmlns="https://www.uniconcreation.com/2021/IvenzaShippingContainer">
<Container>
<Id>I1371851</Id>
<SalesOrderNumber>2012231</SalesOrderNumber>
<ProductionOrderNumber>I2017658</ProductionOrderNumber>
<Name>ACCESSOIRE1</Name>
<SSCC>111</SSCC>
<Barcode>181001371851</Barcode>
<Items>
<Item>
<Id>I8709475</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
<Item>
<Id>I8709476</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
<Container>
<Id>I1371852</Id>
<SalesOrderNumber>2012231</SalesOrderNumber>
<ProductionOrderNumber>I2017658</ProductionOrderNumber>
<Name>PANEEL1</Name>
<SSCC>111</SSCC>
<Barcode>181001371852</Barcode>
<Items>
<Item>
<Id>I8709492</Id>
<SalesOrderRowId>I671068</SalesOrderRowId>
<SalesOrderRowExternalReference>1</SalesOrderRowExternalReference>
<Quantity>1</Quantity>
</Item>
</Items>
</Container>
</Containers>
If the exact order of the child elements of Container does not matter, then it could be even simpler. Instead of:
<xsl:copy-of select="* except (Barcode, Items)"/>
<xsl:copy-of select="$sscc"/>
<xsl:copy-of select="Barcode, Items"/>
you could do:
<xsl:copy-of select="$sscc | *"/>

XSLT, XPath, Using the value of current() to select and sum

I have a list of values that I need to summarize distinct item quantities. A simple version would look like this:
<?xml version="1.0" encoding="UTF-8"?>
<Items xmlns:boomi="http://boomi.com/custom-function">
<Item>
<id>801</id>
<qty>0</qty>
</Item>
<Item>
<id>802</id>
<qty>1</qty>
</Item>
<Item>
<id>802</id>
<qty>1</qty>
</Item>
</Items>
I'm using the following XSLT
<Items>
<xsl:for-each select="distinct-values(/Items/Item/id)" >
<Item>
<id>
<xsl:value-of select="current()" />
</id>
</Item>
</xsl:for-each>
</Items>
To generate the following document
<?xml version="1.0" encoding="UTF-8"?>
<Items xmlns:boomi="http://boomi.com/custom-function">
<Item>
<id>801</id>
</Item>
<Item>
<id>802</id>
</Item>
</Items>
But I also need to include quantities. I've tried a few things, this being the closest, but doesn't work:
<Items>
<xsl:for-each select="distinct-values(/Items/Item/id)" >
<Item>
<id>
<xsl:value-of select="current()" />
<xsl:value-of select="sum(/Items/Item[id=current()]/qty)" />
</id>
</Item>
</xsl:for-each>
</Items>
I think it's not working because current() is an actual node? I'm trying to get to this:
<?xml version="1.0" encoding="UTF-8"?>
<Items xmlns:boomi="http://boomi.com/custom-function">
<Item>
<id>801</id>
<qty>0</qty>
</Item>
<Item>
<id>802</id>
<qty>2</qty>
</Item>
</Items>
Am I going about this in completely the wrong way? Thanks.
Here is the(a) solution using XSLT 2.0 indicated by michael.hor257k
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:boomi="http://boomi.com/custom-function">
<xsl:template match="/">
<Items>
<xsl:for-each-group select="/Items/Item" group-by="id" >
<Item>
<id>
<xsl:value-of select="current-grouping-key()" />
</id>
<qty>
<xsl:value-of select="sum(current-group()/qty)" />
</qty>
</Item>
</xsl:for-each-group>
</Items>
</xsl:template>
</xsl:stylesheet>

XSLT Grouping elements inside two siblings

I'm trying to wrap my head around this and I think the easiest way to explain it is to just show you, below. I've seen this but it doesn't always apply due to the fact I have standalone items also at the end which would match.
The seemingly tricky part is Whatever3 to Whatever6, and then Whatever7 and Whatever8, then finally the new position of Whatever9 - they need to be grouped and the original sequence maintained. (Ignore my naming, there is no way to use xsl:sort)
I've considered xsl:for-each with xsl:if inside, but issue is you can't guarantee how many "groups" vs "non-group" items there are.
Thanks!
XML
<root>
<item>
<position>1</position>
<label>Whatever1</label>
</item>
<item>
<position>2</position>
<label>Whatever2</label>
</item>
<item>
<position>3</position>
<label>Whatever3</label>
<marker id="unique1">start_group</marker>
</item>
<item>
<position>4</position>
<label>Whatever4</label>
</item>
<item>
<position>5</position>
<label>Whatever5</label>
</item>
<item>
<position>6</position>
<label>Whatever6</label>
<marker>last_in_group</marker>
</item>
<item>
<position>7</position>
<label>Whatever7</label>
<marker id="unique2">start_group</marker>
</item>
<item>
<position>8</position>
<label>Whatever8</label>
<marker>last_in_group</marker>
</item>
<item>
<position>9</position>
<label>Whatever9</label>
</item>
</root>
Result
<structure>
<item>
<position>1</position>
<label>Whatever1</label>
</item>
<item>
<position>2</position>
<label>Whatever2</label>
</item>
<group position="3" id="unique1">
<item>
<position>1</position>
<label>Whatever3</label>
</item>
<item>
<position>2</position>
<label>Whatever4</label>
</item>
<item>
<position>3</position>
<label>Whatever5</label>
</item>
<item>
<position>4</position>
<label>Whatever6</label>
</item>
</group>
<group position="4" id="uniqueid2">
<item>
<position>1</position>
<label>Whatever7</label>
</item>
<item>
<position>2</position>
<label>Whatever8</label>
</item>
</group>
<item>
<position>**5**</position>
<label>Whatever9</label>
</item>
</structure>
======================
Here is what I have so far, the only problem I have (besides it being messy) is Whatever4 and Whatever5 are showing up outside the Group.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="root">
<structure>
<xsl:apply-templates select="item[not(marker)] | item[marker='start_group']"/>
</structure>
</xsl:template>
<xsl:template match="item[marker='start_group']">
<group>
<xsl:variable name="currentPosition" select="number(position/text())"/>
<xsl:variable name="lastGroup" select="count(following-sibling::*[local-name() = 'item' and marker='last_in_group'][1]/preceding-sibling::*) + 1"/>
<xsl:attribute name="position">
<xsl:value-of select="$currentPosition"/>
</xsl:attribute>
<xsl:attribute name="id">
<xsl:value-of select="marker/#id"/>
</xsl:attribute>
<item>
<position><xsl:value-of select="number(position/text()) - $currentPosition + 1"/></position>
<label><xsl:value-of select="label/text()"/></label>
</item>
<!-- position() gets reset in for-loop, so need to adjust with outer position -->
<xsl:for-each select="following-sibling::item[(position() + $currentPosition) <= $lastGroup]">
<item>
<position><xsl:value-of select="number(position/text()) - $currentPosition + 1"/></position>
<label><xsl:value-of select="label/text()"/></label>
</item>
</xsl:for-each>
</group>
</xsl:template>
<xsl:template match="item[not(marker)]">
<item>
<position><xsl:value-of select="position/text()"/></position>
<label><xsl:value-of select="label/text()"/></label>
</item>
</xsl:template>
</xsl:stylesheet>
I. XSLT 1.0 Solution:
This transformation:
<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="kFollowing"
match="item[not(marker[. = 'start_group'])
and
preceding-sibling::*[marker][1]/marker = 'start_group'
]"
use="generate-id(preceding-sibling::*
[marker[. = 'start_group']]
[1])"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item[marker[. = 'start_group']]">
<group position="{1 +count(preceding-sibling::*[. = 'start_group'])}"
id="{marker/#id}">
<xsl:copy-of select=".|key('kFollowing', generate-id())"/>
</group>
</xsl:template>
<xsl:template match=
"item[not(marker[. = 'start_group'])
and
preceding-sibling::*[marker][1]/marker = 'start_group'
]"/>
</xsl:stylesheet>
when applied on the provided XML document:
<root>
<item>
<position>1</position>
<label>Whatever1</label>
</item>
<item>
<position>2</position>
<label>Whatever2</label>
</item>
<item>
<position>3</position>
<label>Whatever3</label>
<marker id="unique1">start_group</marker>
</item>
<item>
<position>4</position>
<label>Whatever4</label>
</item>
<item>
<position>5</position>
<label>Whatever5</label>
</item>
<item>
<position>6</position>
<label>Whatever6</label>
<marker>last_in_group</marker>
</item>
<item>
<position>7</position>
<label>Whatever7</label>
<marker id="unique2">start_group</marker>
</item>
<item>
<position>8</position>
<label>Whatever8</label>
<marker>last_in_group</marker>
</item>
<item>
<position>9</position>
<label>Whatever9</label>
</item>
</root>
produces the wanted, correct result:
<root>
<item>
<position>1</position>
<label>Whatever1</label>
</item>
<item>
<position>2</position>
<label>Whatever2</label>
</item>
<group position="1" id="unique1">
<item>
<position>3</position>
<label>Whatever3</label>
<marker id="unique1">start_group</marker>
</item>
<item>
<position>4</position>
<label>Whatever4</label>
</item>
<item>
<position>5</position>
<label>Whatever5</label>
</item>
<item>
<position>6</position>
<label>Whatever6</label>
<marker>last_in_group</marker>
</item>
</group>
<group position="1" id="unique2">
<item>
<position>7</position>
<label>Whatever7</label>
<marker id="unique2">start_group</marker>
</item>
<item>
<position>8</position>
<label>Whatever8</label>
<marker>last_in_group</marker>
</item>
</group>
<item>
<position>9</position>
<label>Whatever9</label>
</item>
</root>
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<root>
<xsl:for-each-group select="item" group-starting-with=
"*[marker eq 'start_group'
or
not(marker)
and
preceding-sibling::*[marker][1]/marker eq 'last_in_group'
]
">
<xsl:choose>
<xsl:when test="current-group()[1]/marker">
<group position=
"{1 +count(current-group()[1]
/preceding-sibling::*
[marker = 'start_group'])}"
id="{marker/#id}">
<xsl:apply-templates select="current-group()"/>
</group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</root>
</xsl:template>
</xsl:stylesheet>
When this XSLT 2.0 transformation is applied on the same XML document (above), the same correct result is produced.

How to sort min value using xslt?

below is my xml file called test.xml
<products>
<supplier>
<supplierid>1001</supplierid>
<totalprice>30</totalprice>
<items>
<item>Pen</item>
<price>10</price>
</items>
<items>
<item>Pencil</item>
<price>5</price>
</items>
<items>
<item>Bag</item>
<price>15</price>
</items>
</supplier>
<supplier>
<supplierid>1001</supplierid>
<totalprice>23</totalprice>
<items>
<item>Pencil</item>
<price>8</price>
</items>
<items>
<item>Pen</item>
<price>5</price>
</items>
<items>
<item>Bag</item>
<price>10</price>
</items>
</supplier>
<supplier>
<supplierid>1001</supplierid>
<totalprice>24</totalprice>
<items>
<item>Paper Box</item>
<price>7</price>
</items>
<items>
<item>Pen</item>
<price>4</price>
</items>
<items>
<item>Bag</item>
<price>13</price>
</items>
</supplier>
<supplier>
<supplierid>1002</supplierid>
<totalprice>26</totalprice>
<items>
<item>Sharpner Box</item>
<price>7</price>
</items>
<items>
<item>Pen</item>
<price>4</price>
</items>
<items>
<item>Bag</item>
<price>15</price>
</items>
</supplier>
</products>
I need to take output like below using xsl 1.0 or 2.0
<products>
<supplier>
<supplierid>1001</supplierid>
<totalprice>23</totalprice>
<items>
<item>Pencil</item>
<price>8</price>
</items>
<items>
<item>Pen</item>
<price>5</price>
</items>
<items>
<item>Bag</item>
<price>10</price>
</items>
</supplier>
<supplier>
<supplierid>1001</supplierid>
<totalprice>24</totalprice>
<items>
<item>Paper Box</item>
<price>7</price>
</items>
<items>
<item>Pen</item>
<price>4</price>
</items>
<items>
<item>Bag</item>
<price>13</price>
</items>
</supplier>
<supplier>
<supplierid>1002</supplierid>
<totalprice>26</totalprice>
<items>
<item>Sharpner Box</item>
<price>7</price>
</items>
<items>
<item>Pen</item>
<price>4</price>
</items>
<items>
<item>Bag</item>
<price>15</price>
</items>
</supplier>
</products>
Sort less price from "same suppliers" for example <supplierid>1001</supplierid>
Based on there sub nodes <items>
1001/23 and 1001/30 is same based on there <items><item>. we need to remove 1001/30 from the list. because it is high price
Based on the assumption that you like to sort suppliers based on their totalprice:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:template match="/">
<products>
<xsl:for-each select="products/supplier">
<xsl:sort select="totalprice" data-type="number" order="ascending"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</products>
</xsl:template>
</xsl:transform>
This will first sort based on supplierid and secondly based on their totalprice
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()">
<xsl:sort select="supplierid"/> <!-- 1st level sorting -->
<xsl:sort select="totalprice"/> <!-- 2nd level sorting -->
<xsl:sort select="price"/> <!-- 3rd level sorting -->
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note: You can use a free online tool called Online XSLT Test Tool to test your xml/xslt files.

extract and move nodes via XSLT

I need to transform the incoming XML so that I can extract all "item" with "categorie" is equal "two" and move these into a separate "records" node with an attribute initialized as type="two".
Here's a sample of the incoming XML.
<datafeed>
<records type="one">
<purchases>
<items>
<item>
<categorie>one</categorie>
<intrant>String</intrant>
</item>
<item>
<categorie>two</categorie>
<intrant>String</intrant>
</item>
<item>
<categorie>one</categorie>
<intrant>String</intrant>
</item>
<item>
<categorie>two</categorie>
<intrant>String</intrant>
</item>
</items>
</purchases>
</records>
<exchange/>
<context/>
<pilotage/>
</datafeed>
Here's what I would like:
<datafeed>
<records type="one">
<purchases>
<items>
<item>
<categorie>one</categorie>
<intrant>String</intrant>
</item>
<item>
<categorie>one</categorie>
<intrant>String</intrant>
</item>
</items>
</purchases>
</records>
<records type="two">
<purchases>
<items>
<item>
<categorie>two</categorie>
<intrant>String</intrant>
</item>
<item>
<categorie>two</categorie>
<intrant>String</intrant>
</item>
</items>
</purchases>
</records>
<exchange/>
<context/>
<pilotage/>
</datafeed>
I now have two "records" both initialize with it's predefined type (always one or two). The records that were extracted, were moved, hence deleted from the original record.
Thanks
This transformation:
<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="kitemByCategory" match="item"
use="categorie"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="records">
<xsl:call-template name="identity"/>
<xsl:variable name="vCat2Items" select=
"key('kitemByCategory', 'two')"/>
<xsl:if test="$vCat2Items">
<records type="two">
<purchases>
<items>
<xsl:copy-of select="$vCat2Items"/>
</items>
</purchases>
</records>
</xsl:if>
</xsl:template>
<xsl:template match="item[categorie = 'two']"/>
</xsl:stylesheet>
when applied on the provided XML document, produces the wanted, correct result:
<datafeed>
<records type="one">
<purchases>
<items>
<item>
<categorie>one</categorie>
<intrant>String</intrant>
</item>
<item>
<categorie>one</categorie>
<intrant>String</intrant>
</item>
</items>
</purchases>
</records>
<records type="two">
<purchases>
<items>
<item>
<categorie>two</categorie>
<intrant>String</intrant>
</item>
<item>
<categorie>two</categorie>
<intrant>String</intrant>
</item>
</items>
</purchases>
</records>
<exchange/>
<context/>
<pilotage/>
</datafeed>
Do note:
The use and overriding of the identity rule.
How items of categorie "two" are excluded from processing by using an empty template matching them.
The use of keys for efficient and convenient locationg of items by categorie.
With this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="itemsBycategorie" match="item" use="categorie"/>
<xsl:template match="#*|node()">
<xsl:param name="items"/>
<xsl:copy>
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="items" select="$items"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="records">
<xsl:variable name="me" select="."/>
<xsl:for-each select="*/*/*[count(.|key('itemsBycategorie',categorie)[1])=1]">
<records type="{categorie}">
<xsl:apply-templates select="$me/node()">
<xsl:with-param name="items" select="key('itemsBycategorie',categorie)"/>
</xsl:apply-templates>
</records>
</xsl:for-each>
</xsl:template>
<xsl:template match="items">
<xsl:param name="items"/>
<xsl:copy>
<xsl:apply-templates select="$items"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Result:
<datafeed>
<records type="one">
<purchases>
<items>
<item>
<categorie>one</categorie>
<intrant>String</intrant>
</item>
<item>
<categorie>one</categorie>
<intrant>String</intrant>
</item>
</items>
</purchases>
</records>
<records type="two">
<purchases>
<items>
<item>
<categorie>two</categorie>
<intrant>String</intrant>
</item>
<item>
<categorie>two</categorie>
<intrant>String</intrant>
</item>
</items>
</purchases>
</records>
<exchange></exchange>
<context></context>
<pilotage></pilotage>
</datafeed>
Note: Muenchian Method of grouping. And "poor man's tunnel" params (Dimitre quot).