Reputation: 1070
I want to write an xsl transformation, but am stuck on the "counter" part. This is basically what I want to do:
Input file:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<Pallets>
<Pallet>
<Line>
<Product>test</Product>
</Line>
<Line>
<Product>test2</Product>
</Line>
</Pallet>
<Pallet>
<Line>
<Product>test_1</Product>
</Line>
<Line>
<Product>test_2</Product>
</Line>
</Pallet>
</Pallets>
</root>
And this is what I want the output to be:
<?xml version="1.0" encoding="utf-8"?>
<Result>
<Pallet>
<ID>2</ID> ==> This is a counter that increments starting from 2
<ID2>1</ID2> ==> Always "1"
<Line>
<ID>3</ID> ==> The counter from above that increments
<ParentID>2</ParentID> ==> PalletID (ID from above the loop)
<Name>test</Name>
</Line>
<Line>
<ID>4</ID> ==> The counter from above that increments
<ParentID>2</ParentID> ==> PalletID
<Name>test2</Name>
</Line>
</Pallet>
<Pallet>
<ID>5</ID> ==> The counter from above that increments
<ID2>1</ID2> ==> Always "1"
<Line>
<ID>6</ID> ==> The counter from above that increments
<ParentID>5</ParentID> ==> PalletID
<Name>test_1</Name>
</Line>
<Line>
<ID>7</ID> ==> The counter from above that increments
<ParentID>5</ParentID> ==> PalletID
<Name>test_2</Name>
</Line>
</Pallet>
</Result>
Can anyone help me with this? This is what I have so far but as you will see, the counter for palletId is not correct. The second PalletID should have ID = 5 instead of 3:
<?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="/">
<Result>
<xsl:for-each select="root/Pallets/Pallet">
<xsl:variable name="counter" select="1" />
<Pallet>
<xsl:variable name="Parentcounter" select="position() + $counter" />
<ID>
<xsl:value-of select="$Parentcounter"/>
</ID>
<ID2>1</ID2>
<xsl:for-each select="Line">
<Line>
<ID>
<xsl:value-of select="$Parentcounter + position()"/>
</ID>
<ParentID>
<xsl:value-of select="$Parentcounter"/>
</ParentID>
<Name>
<xsl:value-of select="Product"/>
</Name>
</Line>
</xsl:for-each>
</Pallet>
</xsl:for-each>
</Result>
</xsl:template>
</xsl:stylesheet>
Thanks in advance.
Upvotes: 1
Views: 1805
Reputation: 163458
Use
<xsl:number level="any" count="Pallet|Line"/>
Because you are starting from 2, you will have to put this in a variable and add one.
Upvotes: 0
Reputation: 70638
You could get the ID of a Pallet element by simply counting the number of preceding Pallet and Line elements
<xsl:variable name="id" select="count(preceding::Pallet|preceding::Line) + 2" />
<ID><xsl:value-of select="$id" /></ID>
If you passed this as parameter in a template matching Line elements, then the ID of the Line element would be as follows (where $parentID contains the id of the parent)
<ID><xsl:value-of select="$ParentID + count(preceding-sibling::Line) + 1" /></ID>
So, the following XSLT should produce the desired output
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Pallets">
<Result>
<xsl:apply-templates select="Pallet" />
</Result>
</xsl:template>
<xsl:template match="Pallet">
<xsl:variable name="id" select="count(preceding::Pallet|preceding::Line) + 2" />
<Pallet>
<ID><xsl:value-of select="$id" /></ID>
<ID2>1</ID2>
<xsl:apply-templates select="Line">
<xsl:with-param name="ParentID" select="$id" />
</xsl:apply-templates>
</Pallet>
</xsl:template>
<xsl:template match="Line">
<xsl:param name="ParentID" />
<Line>
<ID><xsl:value-of select="$ParentID + count(preceding-sibling::Line) + 1" /></ID>
<ParentID><xsl:value-of select="$ParentID" /></ParentID>
<Name><xsl:value-of select="Product" /></Name>
</Line>
</xsl:template>
</xsl:stylesheet>
However, this would not necessarily be that efficient for larger number of Pallets and Items, because it would have to repeatedly count a larger number of preceding elements (essentially counting the same thing over and over again). Another way would to make use of recursive templates, passing in the running total of the ID for the Pallet as parameter each time. Try this XSLT also:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Pallets">
<Result>
<xsl:apply-templates select="Pallet[1]"/>
</Result>
</xsl:template>
<xsl:template match="Pallet">
<xsl:param name="id" select="2"/>
<Pallet>
<ID>
<xsl:value-of select="$id"/>
</ID>
<ID2>1</ID2>
<xsl:apply-templates select="Line[1]">
<xsl:with-param name="parentID" select="$id"/>
</xsl:apply-templates>
</Pallet>
<xsl:apply-templates select="following-sibling::Pallet[1]">
<xsl:with-param name="id" select="$id + count(Line) + 1"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Line">
<xsl:param name="parentID"/>
<xsl:param name="id" select="$parentID + 1"/>
<Line>
<ID>
<xsl:value-of select="$id"/>
</ID>
<ParentID>
<xsl:value-of select="$parentID"/>
</ParentID>
<Name>
<xsl:value-of select="Product" />
</Name>
</Line>
<xsl:apply-templates select="following-sibling::Line[1]">
<xsl:with-param name="parentID" select="$parentID"/>
<xsl:with-param name="id" select="$id + 1"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
This also produces the same output
<Result>
<Pallet>
<ID>2</ID>
<ID2>1</ID2>
<Line>
<ID>3</ID>
<ParentID>2</ParentID>
<Name>test</Name>
</Line>
<Line>
<ID>4</ID>
<ParentID>2</ParentID>
<Name>test2</Name>
</Line>
</Pallet>
<Pallet>
<ID>5</ID>
<ID2>1</ID2>
<Line>
<ID>6</ID>
<ParentID>5</ParentID>
<Name>test_1</Name>
</Line>
<Line>
<ID>7</ID>
<ParentID>5</ParentID>
<Name>test_2</Name>
</Line>
</Pallet>
</Result>
Upvotes: 1