Chinmoy
Chinmoy

Reputation: 1754

XSLT: Copy a group of nodes with incrementing values

I have an XML that looks like this:

<request>
    <MaterialOrderItems>
        <Item>
            <LineId>1</LineId>
            <ParentLineId>1</ParentLineId>
        </Item>
        <Item>
            <LineId>2</LineId>
            <ParentLineId>1</ParentLineId>
        </Item>
        <Item>
            <LineId>3</LineId>
            <ParentLineId>1</ParentLineId>
        </Item>
    </MaterialOrderItems>
</request>

I want to take this XML, and duplicate this Item group (Item[1,2,3]) into multiple copies. For example, if I want 2 copies, my final xml will look like this:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<request>
    <MaterialOrderItems>
    <Item>
      <LineId>1</LineId>
      <ParentLineId>1</ParentLineId>
    </Item>
    <Item>
      <LineId>2</LineId>
      <ParentLineId>1</ParentLineId>
    </Item>
    <Item>
      <LineId>3</LineId>
      <ParentLineId>1</ParentLineId>
    </Item>
    <Item>
      <LineId>4</LineId>
      <ParentLineId>4</ParentLineId>
    </Item>
    <Item>
      <LineId>5</LineId>
      <ParentLineId>4</ParentLineId>
    </Item>
    <Item>
      <LineId>6</LineId>
      <ParentLineId>4</ParentLineId>
    </Item>
    </MaterialOrderItems>
</request>

I have come up with this xslt to do this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns="http://www.w3.org/1999/xhtml" version="1.0">
    <xsl:output encoding="UTF-8" indent="yes" method="xml" standalone="no" 
         omit-xml-declaration="no"/>

<xsl:param name="nmaterials" select="2" />

<xsl:template match="node() | @*">
    <xsl:copy>
        <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
</xsl:template>


<xsl:template match="//MaterialOrderItems" name="order">
    <xsl:param name="material_number" select="1" />

    <xsl:copy>
          <Item>
            <LineId><xsl:value-of select="((($material_number - 1) * 3) + 1)"/></LineId>
            <ParentLineId><xsl:value-of select="((($material_number - 1) * 3) + 1)"/></ParentLineId>
          </Item>
          <Item>
            <LineId><xsl:value-of select="((($material_number - 1) * 3) + 2)"/></LineId>
            <ParentLineId><xsl:value-of select="((($material_number - 1) * 3) + 1)"/></ParentLineId>
          </Item>
          <Item>
            <LineId><xsl:value-of select="((($material_number - 1) * 3) + 3)"/></LineId>
            <ParentLineId><xsl:value-of select="((($material_number - 1) * 3) + 1)"/></ParentLineId>
          </Item>
    </xsl:copy>

    <xsl:if test="$material_number &lt; $nmaterials">
        <xsl:apply-templates select=".">
            <xsl:with-param name="material_number" select="$material_number + 1" />
        </xsl:apply-templates>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

and am getting this output:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<request>
  <MaterialOrderItems>
    <Item>
      <LineId>1</LineId>
      <ParentLineId>1</ParentLineId>
    </Item>
    <Item>
      <LineId>2</LineId>
      <ParentLineId>1</ParentLineId>
    </Item>
    <Item>
      <LineId>3</LineId>
      <ParentLineId>1</ParentLineId>
    </Item>
  </MaterialOrderItems>
  <MaterialOrderItems>
    <Item>
      <LineId>4</LineId>
      <ParentLineId>4</ParentLineId>
    </Item>
    <Item>
      <LineId>5</LineId>
      <ParentLineId>4</ParentLineId>
    </Item>
    <Item>
      <LineId>6</LineId>
      <ParentLineId>4</ParentLineId>
    </Item>
  </MaterialOrderItems>
</request>

I have two questions:

  1. How to get rid of the MaterialOrderItems node repeating here? I want it only once enclosing all the Items.
  2. How to do this in a better way processing the original xml? Currently I am hardcoding the original xml in my XSL template. There should be a better way to do this.

Please help.

Upvotes: 0

Views: 113

Answers (1)

Rupesh_Kr
Rupesh_Kr

Reputation: 3445

Try below code

see transformation at https://xsltfiddle.liberty-development.net/3NSTbeS

    <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output indent="yes"/>

    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:param name="nmaterials" select="2"/>

    <xsl:template match="MaterialOrderItems">
        <xsl:param name="material_number" select="1"/>
        <xsl:variable name="countitem" select="count(Item)"/>
        <xsl:copy>
            <xsl:copy-of select="*"/>
            <xsl:if test="$material_number != $nmaterials">
                <xsl:call-template name="Copy">
                    <xsl:with-param name="data" select="."/>
                    <xsl:with-param name="material_number" select="$material_number + 1"/>
                    <xsl:with-param name="countitem" select="$countitem"/>
                </xsl:call-template>
            </xsl:if>
        </xsl:copy>
    </xsl:template>

    <xsl:template name="Copy">
        <xsl:param name="data"/>
        <xsl:param name="material_number"/>
        <xsl:param name="countitem"/>
        <xsl:for-each select="$data/Item">
            <xsl:variable name="position" select="position()"/>
            <xsl:apply-templates select=".">
                <xsl:with-param name="pos" select="$position"/>
                <xsl:with-param name="material_number" select="$material_number"/>
                <xsl:with-param name="countitem" select="$countitem"/>
            </xsl:apply-templates>
        </xsl:for-each>
        <xsl:if test="$material_number != $nmaterials">
            <xsl:call-template name="Copy">
                <xsl:with-param name="data" select="$data"/>
                <xsl:with-param name="material_number" select="$material_number + 1"/>
                <xsl:with-param name="countitem" select="$countitem"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

    <xsl:template match="Item">
        <xsl:param name="pos"/>
        <xsl:param name="material_number"/>
        <xsl:param name="countitem"/>
        <xsl:copy>
            <LineId>
                <xsl:value-of select="((($material_number - 1) * $countitem) + $pos)"/>
            </LineId>
            <ParentLineId>
                <xsl:value-of select="((($material_number - 1) * $countitem) + 1)"/>
            </ParentLineId>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Related Questions