Home > Back-end >  XSLT How to change values of nodes via for-each loop
XSLT How to change values of nodes via for-each loop

Time:10-14

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>
  • Related