I have a problem with numerating XML nodes via xslt:for-each loop.
Sample XML input:
<body>
<ID>1</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
<body>
<ID>1</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
<body>
<ID>1</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
And I want the output to be like:
<body>
<ID>1</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
<body>
<ID>2</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>2</dummy>
<dummy2>1</dummy2>
</sample>
</body>
<body>
<ID>3</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>3</dummy>
<dummy2>1</dummy2>
</sample>
</body>
I tried with for-each loop but ended up with 1 2 3 values instead of one of them.
Is for-each loop a good approach to the problem or is there better way?
CodePudding user response:
Let's start with a well-formed XML as the input:
XML
<root>
<body>
<ID>1</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
<body>
<ID>1</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
<body>
<ID>1</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
</root>
Now, if you only wanted to renumber the body
element by changing the value of ID
, then you could do simply:
XSLT 1.0 [A]
<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:template match="/root">
<xsl:copy>
<xsl:for-each select="body">
<xsl:copy>
<ID>
<xsl:value-of select="position()"/>
</ID>
<xsl:copy-of select="*[not(self::ID)]"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
to get:
Result [A]
<?xml version="1.0" encoding="UTF-8"?>
<root>
<body>
<ID>1</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
<body>
<ID>2</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
<body>
<ID>3</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
</root>
However, it seems you also want to have the same number in the dummy
element, which makes this more complicated. Here's one way you could look at it:
XSLT 1.0 [B]
<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="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ID | dummy">
<xsl:copy>
<xsl:number count="body"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
And here's another:
XSLT 2.0 [B]
<xsl:stylesheet version="2.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="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="body">
<xsl:copy>
<xsl:apply-templates>
<xsl:with-param name="num" select="position()" tunnel="yes"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="ID | dummy">
<xsl:param name="num" tunnel="yes"/>
<xsl:copy>
<xsl:value-of select="$num"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In both cases. the result will be:
Result [B]
<?xml version="1.0" encoding="UTF-8"?>
<root>
<body>
<ID>1</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>1</dummy>
<dummy2>1</dummy2>
</sample>
</body>
<body>
<ID>2</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>2</dummy>
<dummy2>1</dummy2>
</sample>
</body>
<body>
<ID>3</ID>
<foo>1</foo>
<foo2>1</foo2>
<sample>
<dummy>3</dummy>
<dummy2>1</dummy2>
</sample>
</body>
</root>