0

I am trying to copy one of the nested element multiple times. I search and came across XSLT: copy object xml multiple times while incrementing attribute and value. This is close but not the answer that I'm looking for. This is what I'm trying to do

XML Input:

<Company xmlns="http://test.com" >
    <Group document="dump" >
        <dump>asdfasd</dump>
        <dump2>asdfasdf</dump2>
        <Person>
            <record>1</record>
            <dump2>asdfasdf</dump2>
            <properties>
                <name>John</name>
                <number>1</number>
            </properties>
        </Person>
    </Group>
</Company>

And output to something like:

<Company xmlns="http://test.com">
   <Group document="dump">
      <dump>asdfasd</dump>
      <dump2>asdfasdf</dump2>
      <Person>
         <record>1</record>
         <dump2>asdfasdf</dump2>
         <properties>
            <name>John</name>
            <number>1</number>
         </properties>
      </Person>
      ...
      <Person>
         <record>n</record>
         <dump2>asdfasdf</dump2>
         <properties>
            <name>John</name>
            <number>n</number>
         </properties>
      </Person>
   </Group>
</Company>

With my xslt:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:t="http://test.com"
>
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>

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

     <xsl:template match="node()|@*">
      <xsl:param name="pPosition" select="1"/>
      <xsl:copy>
       <xsl:apply-templates select="node()|@*">
         <xsl:with-param name="pPosition" select="$pPosition"/>
       </xsl:apply-templates>
      </xsl:copy>
     </xsl:template>

     <xsl:template match="t:Person">
         <xsl:copy>
             <xsl:copy-of select="@*"/>
             <xsl:call-template name="applyNTimes">
                 <xsl:with-param name="pTimes" select="$pTimes"/>
                 <xsl:with-param name="pPosition" select="1"/>
             </xsl:call-template>
          </xsl:copy>
     </xsl:template>

     <xsl:template name="applyNTimes">
         <xsl:param name="pTimes" select="0"/>
         <xsl:param name="pPosition" select="1"/>

         <xsl:if test="$pTimes > 0">
             <xsl:choose>
             <xsl:when test="$pTimes = 1">
                 <xsl:apply-templates select="*">
                 <xsl:with-param name="pPosition" select="$pPosition"/>
                 </xsl:apply-templates>
             </xsl:when>
             <xsl:otherwise>
                 <xsl:variable name="vHalf" select="floor($pTimes div 2)"/>

                 <xsl:call-template name="applyNTimes">
                 <xsl:with-param name="pTimes" select="$vHalf"/>
                 <xsl:with-param name="pPosition" select="$pPosition"/>
                 </xsl:call-template>

                 <xsl:call-template name="applyNTimes">
                 <xsl:with-param name="pTimes" select="$pTimes - $vHalf"/>
                 <xsl:with-param name="pPosition" select="$pPosition + $vHalf"/>
                 </xsl:call-template>
             </xsl:otherwise>
             </xsl:choose>
         </xsl:if>
     </xsl:template>

     <xsl:template match="t:record">
         <xsl:param name="pPosition" select="1"/>

         <xsl:copy>
              <xsl:value-of select="$pPosition"/>
          </xsl:copy>
     </xsl:template>

     <xsl:template match="t:number">
          <xsl:param name="pPosition" select="1"/>
            <xsl:copy>
              <xsl:value-of select="$pPosition"/>
            </xsl:copy>
     </xsl:template>
</xsl:stylesheet>

This is what I have

<Company xmlns="http://test.com">
   <Group document="dump">
      <dump>asdfasd</dump>
      <dump2>asdfasdf</dump2>
      <Person position="1">
         <record>1</record>
         <dump2>asdfasdf</dump2>
         <properties>
            <name>John</name>
            <number>1</number>
         </properties>
         <record>2</record>
         <dump2>asdfasdf</dump2>
         <properties>
            <name>John</name>
            <number>2</number>
         </properties>
      </Person>
   </Group>
</Company>

How do I prevent Person element from disappearing when it gets copied?

T.J
  • 3
  • 3

1 Answers1

0

The solution in this case is to move the xsl:copy for the Person from the template matching t:Person and into the applyNTimes template (in the case when pTimes is 1).

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:t="http://test.com" version="1.0">
   <xsl:output omit-xml-declaration="yes" indent="yes"></xsl:output>
   <xsl:strip-space elements="*"></xsl:strip-space>
   <xsl:param name="pTimes" select="2"></xsl:param>

   <xsl:template match="node()|@*">
      <xsl:param name="pPosition" select="1"></xsl:param>
      <xsl:copy>
         <xsl:apply-templates select="node()|@*">
            <xsl:with-param name="pPosition" select="$pPosition"></xsl:with-param>
         </xsl:apply-templates>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="t:Person">
      <xsl:call-template name="applyNTimes">
         <xsl:with-param name="pTimes" select="$pTimes"></xsl:with-param>
         <xsl:with-param name="pPosition" select="1"></xsl:with-param>
      </xsl:call-template>
   </xsl:template>

   <xsl:template name="applyNTimes">
      <xsl:param name="pTimes" select="0"></xsl:param>
      <xsl:param name="pPosition" select="1"></xsl:param>
      <xsl:if test="$pTimes &gt; 0">
         <xsl:choose>
            <xsl:when test="$pTimes = 1">
               <xsl:copy>
                  <xsl:apply-templates select="*">
                     <xsl:with-param name="pPosition" select="$pPosition"></xsl:with-param>
                  </xsl:apply-templates>
               </xsl:copy>
            </xsl:when>
            <xsl:otherwise>
               <xsl:variable name="vHalf" select="floor($pTimes div 2)"></xsl:variable>
               <xsl:call-template name="applyNTimes">
                  <xsl:with-param name="pTimes" select="$vHalf"></xsl:with-param>
                  <xsl:with-param name="pPosition" select="$pPosition"></xsl:with-param>
               </xsl:call-template>
               <xsl:call-template name="applyNTimes">
                  <xsl:with-param name="pTimes" select="$pTimes - $vHalf"></xsl:with-param>
                  <xsl:with-param name="pPosition" select="$pPosition + $vHalf"></xsl:with-param>
               </xsl:call-template>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:if>
   </xsl:template>

   <xsl:template match="t:record|t:number">
      <xsl:param name="pPosition" select="1"></xsl:param>
      <xsl:copy>
         <xsl:value-of select="$pPosition"></xsl:value-of>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

Note that, if you could use XSLT 2.0, then things become much easier due the support of a "for 1 to n" construct (and the existences of "tunneling" to avoid having to explicitly pass around parameters in every single template).

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:t="http://test.com" 
                version="2.0">

   <xsl:output omit-xml-declaration="yes" indent="yes"></xsl:output>
   <xsl:strip-space elements="*"></xsl:strip-space>

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

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

   <xsl:template match="t:Person">
      <xsl:variable name="person" select="." />
      <xsl:for-each select="1 to $pTimes">
        <xsl:apply-templates select="$person" mode="repeat">
           <xsl:with-param name="pPosition" select="position()" tunnel="yes" />
        </xsl:apply-templates>
      </xsl:for-each>
   </xsl:template>

   <xsl:template match="t:Person" mode="repeat">
     <xsl:copy>
       <xsl:apply-templates />
     </xsl:copy>
   </xsl:template>

   <xsl:template match="t:record|t:number">
      <xsl:param name="pPosition" select="1" tunnel="yes"></xsl:param>
      <xsl:copy>
         <xsl:value-of select="$pPosition"></xsl:value-of>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>
Tim C
  • 70,053
  • 14
  • 74
  • 93