XSLT Mapping Transformation - xslt

Iam novice to XSLT mapping and looking for help to achieve desired output. I want to copy Input XML as is in to output.
I have tried using xsl:copy-of select which is adding an empty xmls=”” attribute in to /Inventory/Action which is not acceptable by target.
Also When I try xsl:for-each using below XSLT values for elements like AdAction , unit<4567 7896> and ID<12345 67890> QU<200 100> are repeating for each iteration .
Input XML.
<?xml version="1.0" encoding="UTF-8"?>
<Body>
<Inventory>
<Action MessageType="hello" Type="hi" Action="hand" InventoryState="avail">
<DateTime/>
<Ad Action="Set">
<Position>
<Unit TypeCode="abc">4567</Unit>
<Item>
<ID>12345</ID>
</Item>
</Position>
<Qu>200</Qu>
</Ad>
</Action>
<Action MessageType="hello" Type="hi" Action="hand" InventoryState="avail">
<DateTime/>
<Ad Action="Set">
<Position>
<Unit TypeCode="abc">7896</Unit>
<Item>
<ID>67890</ID>
</Item>
</Position>
<Qu>100</Qu>
</Ad>
</Action>
</Inventory>
XSLT mapping I have used.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/XSL/Format" exclude-result-prefixes="xsl">
<xsl:output method="xml" version="1.0" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<Body>
<xsl:for-each select="//Action">
<Action>
<xsl:attribute name="Type">hello</xsl:attribute>
<xsl:attribute name="AType">hi</xsl:attribute>
<xsl:attribute name="Action">hand</xsl:attribute>
<xsl:attribute name="State">avail</xsl:attribute>
<DateTime>DateTime</DateTime>
<Ad>
<xsl:attribute name="Action"><xsl:value-of select="//Ad/#Action"/></xsl:attribute>
<Position>
<Unit>
<xsl:attribute name="TypeCode">Store</xsl:attribute>
</Unit>
<Item>
<ID>
<xsl:value-of select="//Action/Ad/Position/Item/ID"/>
</ID>
</Item>
</Position>
<Qu>
<xsl:value-of select="//Action/Ad/Qu"/>
</Qu>
</Ad>
</Action>
</xsl:for-each>
</Body>
</xsl:template>
Output:
<Body xmlns="http://www.w3.org/1999/XSL/Format">
<Action Type="hello" AType="hi" Action="hand" State="avail">
<DateTime>DateTime</DateTime>
<Ad Action="Set Set">
<Position>
<Unit TypeCode="Store"/>
<Item>
<ID>12345 67890</ID>
</Item>
</Position>
<Qu>200 100</Qu>
</Ad>
</Action>
<Action Type="hello" AType="hi" Action="hand" State="avail">
<DateTime>DateTime</DateTime>
<Ad Action="Set Set">
<Position>
<Unit TypeCode="Store"/>
<Item>
<ID>12345 67890</ID>
</Item>
</Position>
<Qu>200 100</Qu>
</Ad>
</Action>
Desired Output no repeating Values.
<Body xmlns="http://www.w3.org/1999/XSL/Format">
<Action Type="hello" AType="hi" Action="hand" State="avail">
<DateTime>DateTime</DateTime>
<Ad Action="Set">
<Position>
<Unit TypeCode="Store"/>
<Item>
<ID>12345</ID>
</Item>
</Position>
<Qu>200</Qu>
</Ad>
</Action>
<Action Type="hello" AType="hi" Action="hand" State="avail">
<DateTime>DateTime</DateTime>
<Ad Action="Set">
<Position>
<Unit TypeCode="Store"/>
<Item>
<ID>67890</ID>
</Item>
</Position>
<Qu>100</Qu>
</Ad>
</Action>

Change:
<xsl:value-of select="//Action/Ad/Position/Item/ID"/>
to:
<xsl:value-of select="Ad/Position/Item/ID"/>
What you have is an absolute path, starting from the root - and it will select every item ID in the entire document. What you need is a relative path, starting from the current Action.
Similarly, change:
<xsl:value-of select="//Action/Ad/Qu"/>
to:
<xsl:value-of select="Ad/Qu"/>

Related

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>

Find unique child nodes of an element

Need to write an xslt file. Below is the input file:
<assets>
<item>
<child1>some text</child1>
<child2>some text</child2>
<child3>some text</child3>
<child4>some text</child4>
</item>
<item>
<child1>some text</child1>
<child2>some text</child2>
<childx>some text</childx>
</item>
<item>
<child1>some text</child1>
<childx>some text</childx>
<childy>some text</childy>
<childz>some text</childz>
</item>
</assets>
I need to find out all the unique child names of assets/item. The number of children and child name is dynamic under the element (item)
The Output should be as below:
<item>
<columns>
<columnname>child1</columnname>
<columnname>child2</columnname>
<columnname>child3</columnname>
<columnname>child4</columnname>
<columnname>childx</columnname>
<columnname>childy</columnname>
<columnname>childz</columnname>
</columns>
</item>
You can use Muenchian grouping on the element names - something like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="elements" match="*" use="local-name()" />
<xsl:template match="/">
<item>
<xsl:apply-templates select="assets/item" />
</item>
</xsl:template>
<xsl:template match="item">
<xsl:for-each select="*[count(.|key('elements', local-name())[1]) = 1]">
<columnname>
<xsl:value-of select="name()"/>
</columnname>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Will get you this:
<item>
<columnname>child1</columnname>
<columnname>child2</columnname>
<columnname>child3</columnname>
<columnname>child4</columnname>
<columnname>childx</columnname>
<columnname>childy</columnname>
<columnname>childz</columnname>
</item>
you can use this
<xsl:key name="child" match="item/*" use="name()"/>
<xsl:template match="/">
<item>
<columns>
<xsl:for-each select="//item/*[count(.|key('child', name())[1]) = 1]">
<columnname><xsl:value-of select="name()"/></columnname>
</xsl:for-each>
</columns>
</item>
</xsl:template>
output
<item>
<columns>
<columnname>child1</columnname>
<columnname>child2</columnname>
<columnname>child3</columnname>
<columnname>child4</columnname>
<columnname>childx</columnname>
<columnname>childy</columnname>
<columnname>childz</columnname>
</columns>
</item>

XSTL - maintain sequence

I have an the input xml as:
<group>
<item>
<id>item 1</id>
<CategoryName>blue</CategoryName>
<id>item 2</id>
<CategoryName>orange</CategoryName>
<id>item 3</id>
<CategoryName>green</CategoryName>
</item>
</group>
and I want to transform it to
<group>
<item>
<itemNode><id>item 1</id><itemNode>
<Color>blue</Color>
<itemNode><id>item 2</id><itemNode>
<Color>orange</Color>
<itemNode><id>item 3</id><itemNode>
<Color>green</Color>
</item>
When I use xpath, it shows itemNodes tags first and then Color tags but not in the sequence the data was orignally in.
Any ideas how I can transform but still maintain the sequence?
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="item">
<group>
<item>
<xsl:for-each select="id">
<xsl:variable name="pos" select="position()" />
<itemNode><id><xsl:value-of select="."/></id></itemNode>
<color><xsl:value-of select="../CategoryName[$pos]"/></color>
</xsl:for-each>
</item>
</group>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0: apply-templates and template mode

I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<Order>
<Item>
<RECORD_ID>RECORD_ID</RECORD_ID>
<ENTITY_CODE>ENTITY_CODE</ENTITY_CODE>
<USER_CODE>USER_CODE</USER_CODE>
<RECORD_DATE>RECORD_DATE</RECORD_DATE>
<ITEM_CODE>ITEM_CODE</ITEM_CODE>
<LINE_QUANTITY>LINE_QUANTITY</LINE_QUANTITY>
<LINE_FREE_STOCK>LINE_FREE STOCK</LINE_FREE_STOCK>
<LINE_PRICE>LINE_PRICE</LINE_PRICE>
<LINE_DISCOUNT_PERCENT>LINE_DISCOUNT PERCENT</LINE_DISCOUNT_PERCENT>
</Item>
<Item>
<RECORD_ID>9046</RECORD_ID>
<ENTITY_CODE>12010601</ENTITY_CODE>
<USER_CODE>122</USER_CODE>
<RECORD_DATE>2011-08-24</RECORD_DATE>
<ITEM_CODE>804-008165</ITEM_CODE>
<LINE_QUANTITY>2</LINE_QUANTITY>
<LINE_FREE_STOCK>1</LINE_FREE_STOCK>
</Item>
<Item>
<RECORD_ID>9046</RECORD_ID>
<ENTITY_CODE>12010601</ENTITY_CODE>
<USER_CODE>122</USER_CODE>
<RECORD_DATE>2011-08-24</RECORD_DATE>
<ITEM_CODE>804-008161</ITEM_CODE>
<LINE_QUANTITY>1</LINE_QUANTITY>
<LINE_FREE_STOCK>1</LINE_FREE_STOCK>
</Item>
<Item>
<RECORD_ID>9046</RECORD_ID>
<ENTITY_CODE>12010601</ENTITY_CODE>
<USER_CODE>122</USER_CODE>
<RECORD_DATE>2011-08-24</RECORD_DATE>
<ITEM_CODE>804-008225</ITEM_CODE>
<LINE_QUANTITY>5</LINE_QUANTITY>
</Item>
</Order>
Sometimes within the item tag I have the element <LINE_FREE_STOCK>. If that occurs I have to create an additional position in the output XML.
Now I came up with this style sheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="UTF-8" method="xml" indent="yes"/>
<xsl:template match="/">
<ORDERS05>
<IDOC BEGIN="1">
<xsl:apply-templates select="Order"/>
</IDOC>
</ORDERS05>
</xsl:template>
<xsl:template match="Order">
<Header>
<xsl:value-of select="'some header data'"/>
</Header>
<xsl:apply-templates select="Item[position() >1]"/>
<xsl:apply-templates select="Item[position() >1 and child::LINE_FREE_STOCK]" mode="freestock"/>
</xsl:template>
<xsl:template match="Item">
<position>
<item>
<number><xsl:value-of select="ITEM_CODE"/></number>
<quantity><xsl:value-of select="LINE_QUANTITY"/></quantity>
</item>
</position>
</xsl:template>
<xsl:template match="Item[position() >1 and child::LINE_FREE_STOCK]" mode="freestock">
<position>
<item>
<number><xsl:value-of select="ITEM_CODE"/></number>
<freestock_quant><xsl:value-of select="LINE_FREE_STOCK"/></freestock_quant>
</item>
</position>
</xsl:template>
</xsl:stylesheet>
It creates this (simplified) wanted output:
<?xml version="1.0" encoding="UTF-8"?>
<ORDERS05>
<IDOC BEGIN="1">
<Header>some header data</Header>
<position>
<item>
<number>804-008165</number>
<quantity>2</quantity>
</item>
</position>
<position>
<item>
<number>804-008161</number>
<quantity>1</quantity>
</item>
</position>
<position>
<item>
<number>804-008225</number>
<quantity>5</quantity>
</item>
</position>
<position>
<item>
<number>804-008165</number>
<freestock_quant>1</freestock_quant>
</item>
</position>
<position>
<item>
<number>804-008161</number>
<freestock_quant>1</freestock_quant>
</item>
</position>
</IDOC>
</ORDERS05>
804-008165 and 804-008161 show up twice - once as a standard item and once as the free stock item with the respective quantities.
But did I forget anything here? Is there some sort of pitfall I don't see?
Is that XSLT robust enough?
As others have noted, the problem is in this code:
<xsl:apply-templates select="Item"/>
<xsl:apply-templates select="Item[child::LINE_FREE_STOCK]" mode="freestock"/>
If there is a child Item that has a child LINE_FREE_STOCK, templates would be applied on this Item element twice -- here is how you get the repetitions in the output.
The transformation can be significantly shortened and it doesn't need modes or explicit conditional instructions at all:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<ORDERS05>
<IDOC BEGIN="1">
<xsl:apply-templates select="Order"/>
</IDOC>
</ORDERS05>
</xsl:template>
<xsl:template match="Order">
<Header>
<xsl:value-of select="'some header data'"/>
</Header>
<xsl:apply-templates select="Item[position() >1]"/>
</xsl:template>
<xsl:template match="Item">
<position>
<item>
<number>
<xsl:value-of select="ITEM_CODE"/>
</number>
<xsl:apply-templates select=
"self::node()[not(LINE_FREE_STOCK)]/LINE_QUANTITY
|
LINE_FREE_STOCK"/>
</item>
</position>
</xsl:template>
<xsl:template match="LINE_QUANTITY">
<quantity>
<xsl:value-of select="."/>
</quantity>
</xsl:template>
<xsl:template match="LINE_FREE_STOCK">
<freestock_quant>
<xsl:value-of select="."/>
</freestock_quant>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Order>
<Item>
<RECORD_ID>RECORD_ID</RECORD_ID>
<ENTITY_CODE>ENTITY_CODE</ENTITY_CODE>
<USER_CODE>USER_CODE</USER_CODE>
<RECORD_DATE>RECORD_DATE</RECORD_DATE>
<ITEM_CODE>ITEM_CODE</ITEM_CODE>
<LINE_QUANTITY>LINE_QUANTITY</LINE_QUANTITY>
<LINE_FREE_STOCK>LINE_FREE STOCK</LINE_FREE_STOCK>
<LINE_PRICE>LINE_PRICE</LINE_PRICE>
<LINE_DISCOUNT_PERCENT>LINE_DISCOUNT PERCENT</LINE_DISCOUNT_PERCENT>
</Item>
<Item>
<RECORD_ID>9046</RECORD_ID>
<ENTITY_CODE>12010601</ENTITY_CODE>
<USER_CODE>122</USER_CODE>
<RECORD_DATE>2011-08-24</RECORD_DATE>
<ITEM_CODE>804-008165</ITEM_CODE>
<LINE_QUANTITY>2</LINE_QUANTITY>
<LINE_FREE_STOCK>1</LINE_FREE_STOCK>
</Item>
<Item>
<RECORD_ID>9046</RECORD_ID>
<ENTITY_CODE>12010601</ENTITY_CODE>
<USER_CODE>122</USER_CODE>
<RECORD_DATE>2011-08-24</RECORD_DATE>
<ITEM_CODE>804-008161</ITEM_CODE>
<LINE_QUANTITY>1</LINE_QUANTITY>
<LINE_FREE_STOCK>1</LINE_FREE_STOCK>
</Item>
<Item>
<RECORD_ID>9046</RECORD_ID>
<ENTITY_CODE>12010601</ENTITY_CODE>
<USER_CODE>122</USER_CODE>
<RECORD_DATE>2011-08-24</RECORD_DATE>
<ITEM_CODE>804-008225</ITEM_CODE>
<LINE_QUANTITY>5</LINE_QUANTITY>
</Item>
</Order>
the wanted, correct result is produced:
<ORDERS05>
<IDOC BEGIN="1">
<Header>some header data</Header>
<position>
<item>
<number>804-008165</number>
<freestock_quant>1</freestock_quant>
</item>
</position>
<position>
<item>
<number>804-008161</number>
<freestock_quant>1</freestock_quant>
</item>
</position>
<position>
<item>
<number>804-008225</number>
<quantity>5</quantity>
</item>
</position>
</IDOC>
</ORDERS05>
This is because you have two match templates for Item:
<xsl:template match="Item">
<xsl:if test="position() > 1">
<position>
<item>
<number><xsl:value-of select="ITEM_CODE"/></number>
<quantity><xsl:value-of select="LINE_QUANTITY"/></quantity>
</item>
</position>
</xsl:if>
</xsl:template>
<xsl:template match="Item[child::LINE_FREE_STOCK]" mode="freestock">
<xsl:if test="position() > 1">
<position>
<item>
<number><xsl:value-of select="ITEM_CODE"/></number>
<freestock_quant><xsl:value-of select="LINE_FREE_STOCK"/></freestock_quant>
</item>
</position>
</xsl:if>
</xsl:template>
First the default Item template matches and then the Item's with LINE_FREE_STOCK also matches the Item with child LINE_FREE_STOCK template, hence the duplicate for Item's with LINE_FREE_STOCK.
Instead why not just use one template, like this:
<xsl:template match="Item">
<xsl:if test="position() > 1">
<position>
<item>
<number><xsl:value-of select="ITEM_CODE"/></number>
<xsl:choose>
<xsl:when test="child::LINE_FREE_STOCK">
<freestock_quant><xsl:value-of select="LINE_FREE_STOCK"/></freestock_quant>
</xsl:when>
<xsl:otherwise>
<quantity><xsl:value-of select="LINE_QUANTITY"/></quantity>
</xsl:otherwise>
</xsl:choose>
</item>
</position>
</xsl:if>
</xsl:template>
Using the single template your Order template is also simplified:
<xsl:template match="Order">
<Header>
<xsl:value-of select="'some header data'"/>
</Header>
<xsl:apply-templates select="Item"/>
</xsl:template>
This way you do not need to use Modes either.
It's not clear what is the wanted output. Perhaps you want:
<xsl:apply-templates select="Item[not(LINE_FREE_STOCK)"/>
<xsl:apply-templates select="Item[LINE_FREE_STOCK]" mode="freestock"/>
in place of your
<xsl:apply-templates select="Item"/>
<xsl:apply-templates select="Item[child::LINE_FREE_STOCK]" mode="freestock"/>
You would need an addition filtering
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="UTF-8" method="xml" indent="yes"/>
<xsl:template match="/">
<ORDERS05>
<IDOC BEGIN="1">
<xsl:apply-templates select="Order"/>
</IDOC>
</ORDERS05>
</xsl:template>
<xsl:template match="Order">
<Header>
<xsl:value-of select="'some header data'"/>
</Header>
<xsl:apply-templates select="Item[not(child::LINE_FREE_STOCK)]"/>
<xsl:apply-templates select="Item[child::LINE_FREE_STOCK]" mode="freestock"/>
</xsl:template>
<xsl:template match="Item">
<xsl:if test="position() > 1">
<position>
<item>
<number><xsl:value-of select="ITEM_CODE"/></number>
<quantity><xsl:value-of select="LINE_QUANTITY"/></quantity>
</item>
</position>
</xsl:if>
</xsl:template>
<xsl:template match="Item[child::LINE_FREE_STOCK]" mode="freestock">
<xsl:if test="position() > 1">
<position>
<item>
<number><xsl:value-of select="ITEM_CODE"/></number>
<freestock_quant><xsl:value-of select="LINE_FREE_STOCK"/></freestock_quant>
</item>
</position>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Split XML file into multiple files based on a threshold value

I need to split the following XML file based on a predetermined value, for this example, lets assume I want to limit the "Item" node to three (3) within each file created.
Here's a sample input XML file:
<Items>
<Item>
<Title>Title 1</Title>
<DueDate>01-02-2008</DueDate>
</Item>
<Item>
<Title>Title 2</Title>
<DueDate>01-02-2009</DueDate>
</Item>
<Item>
<Title>Title 3</Title>
<DueDate>01-02-2010</DueDate>
</Item>
<Item>
<Title>Title 4</Title>
<DueDate>01-02-2011</DueDate>
</Item>
<Item>
<Title>Title 5</Title>
<DueDate>01-02-2012</DueDate>
</Item>
<Item>
<Title>Title 6</Title>
<DueDate>01-02-2013</DueDate>
</Item>
<Item>
<Title>Title 7</Title>
<DueDate>01-02-2013</DueDate>
</Item>
</Items>
The desired output based on the threshold value of 3, would be three files, two of which contain 3 "Item", and the last one containing the remaining "items", which would be one.
Here's a sample of my XSLT which does allow me to split them for each item, which results into seven separate files, however, what I desire is to limit the size of the file based on a certain limit of "Item" node per file.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" indent="yes" name="xml" />
<xsl:template match="/">
<xsl:for-each select="//Item">
<xsl:variable name="nTitle" select="Title"/>
<xsl:variable name="filename" select="concat('Items\',$nTitle,'-','.xml')" />
<xsl:value-of select="$filename" />
<xsl:result-document href="{$filename}" format="xml">
<xsl:copy-of select="."/>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This stylesheet:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="pItemsNumber" select="3"/>
<xsl:template match="Items">
<xsl:for-each-group select="Item"
group-adjacent="(position()-1) idiv $pItemsNumber">
<xsl:result-document href="Items\{current-grouping-key()}.xml">
<Items>
<xsl:copy-of select="current-group()"/>
</Items>
</xsl:result-document>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Item>
<Title>Title 1</Title>
<DueDate>01-02-2008</DueDate>
</Item>
<Item>
<Title>Title 2</Title>
<DueDate>01-02-2009</DueDate>
</Item>
<Item>
<Title>Title 3</Title>
<DueDate>01-02-2010</DueDate>
</Item>
</Items>
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Item>
<Title>Title 4</Title>
<DueDate>01-02-2011</DueDate>
</Item>
<Item>
<Title>Title 5</Title>
<DueDate>01-02-2012</DueDate>
</Item>
<Item>
<Title>Title 6</Title>
<DueDate>01-02-2013</DueDate>
</Item>
</Items>
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Item>
<Title>Title 7</Title>
<DueDate>01-02-2013</DueDate>
</Item>
</Items>
Edit: Oops!
This transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pSplitNum" select="3"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Item[position() mod $pSplitNum eq 1]">
<xsl:result-document href=
"file{position()}-{min((position()+$pSplitNum -1, count(/*/Item)))}.xml">
<Items>
<xsl:call-template name="identity"/>
<xsl:apply-templates mode="copy" select=
"following-sibling::Item[position() lt $pSplitNum]"/>
</Items>
</xsl:result-document>
</xsl:template>
<xsl:template match="/*"><xsl:apply-templates/></xsl:template>
<xsl:template match="Item[position() mod $pSplitNum ne 1]"/>
</xsl:stylesheet>
when applied on the provided XML document:
<Items>
<Item>
<Title>Title 1</Title>
<DueDate>01-02-2008</DueDate>
</Item>
<Item>
<Title>Title 2</Title>
<DueDate>01-02-2009</DueDate>
</Item>
<Item>
<Title>Title 3</Title>
<DueDate>01-02-2010</DueDate>
</Item>
<Item>
<Title>Title 4</Title>
<DueDate>01-02-2011</DueDate>
</Item>
<Item>
<Title>Title 5</Title>
<DueDate>01-02-2012</DueDate>
</Item>
<Item>
<Title>Title 6</Title>
<DueDate>01-02-2013</DueDate>
</Item>
<Item>
<Title>Title 7</Title>
<DueDate>01-02-2013</DueDate>
</Item>
</Items>
produces the wanted three xml files:
Saxon 9.1.0.5J from Saxonica
Java version 1.6.0_22
Stylesheet compilation time: 645 milliseconds
Processing file:/C:/Program%20Files/Java/jre6/bin/marrowtr.xml
Building tree for file:/C:/Program%20Files/Java/jre6/bin/marrowtr.xml using class net.sf.saxon.tinytree.TinyBuilder
Tree built in 10 milliseconds
Tree size: 38 nodes, 119 characters, 0 attributes
Loading net.sf.saxon.event.MessageEmitter
Writing to file:/C:/Program%20Files/Java/jre6/bin/file1-3.xml
Writing to file:/C:/Program%20Files/Java/jre6/bin/file4-6.xml
Writing to file:/C:/Program%20Files/Java/jre6/bin/file7-7.xml
Execution time: 101 milliseconds
Memory used: 11453088
NamePool contents: 20 entries in 20 chains. 6 prefixes, 7 URIs
Do note:
This is a simple application of the identity rule pattern.
Every Item starting a new file is matched and it causes the wrapping in a top element, processing of itself and the next $pSplitNum -1 (or whatever remains in the last group), and outputting this as a single result-document (file).
The name of every file created is: "filex-y.xml", where x and y are the starting and ending indexes of the Item elements written in the file.
Every Item that isn't starting a new file is deleted by an empty matching template. Such elements are processed in "copy" mode.
You could implement a counter that is declared outside of your loop. When the counter hits 3, reset it and set a new filename. Otherwise, increment and append to the existing filename.