XSLT Expand input XML using a for-loop type operation -


i'm new xslt, forgive ignorance, if have input xml this:

<doc>  <stuff>    <for var="i" from="1" to="2">       <item>$(i)></item>       <for var="j" from="2" to="4">         <inneritem>$(j)</inneritem>       </for>     </for>       </stuff> </doc> 

i want use transform have output xml expanded this:

<doc>  <stuff>    <item>1</item>      <inneritem>2</inneritem>      <inneritem>3</inneritem>      <inneritem>4</inneritem>    <item>2</item>      <inneritem>2</inneritem>      <inneritem>3</inneritem>      <inneritem>4</inneritem>  </stuff> </doc> 

all i've got this: next?

<?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="@* | node()">         <xsl:copy>                      <xsl:apply-templates select="@* | node()"/>         </xsl:copy>     </xsl:template>    <xsl:template match="for">     <xsl:variable name="from" select="@from" />     <xsl:variable name="to" select="@to" />     <xsl:copy>       <xsl:apply-templates select="@* | node()"/>     </xsl:copy>       </xsl:template> </xsl:stylesheet> 

here simpler (no explicit conditional instructions, no user-defined functions, no modes) , shorter, working transformation:

<xsl:stylesheet version="2.0"  xmlns:xsl="http://www.w3.org/1999/xsl/transform">  <xsl:output omit-xml-declaration="yes" indent="yes"/>  <xsl:strip-space elements="*"/>   <xsl:template match="node()|@*">   <xsl:param name="pvars" as="element()*"/>   <xsl:copy>    <xsl:apply-templates select="node()|@*">      <xsl:with-param name="pvars" select="$pvars"/>    </xsl:apply-templates>   </xsl:copy>  </xsl:template>   <xsl:template match="for">   <xsl:param name="pvars" as="element()*"/>    <xsl:variable name="vcurrentfor" select="."/>    <xsl:for-each select="@from @to">       <xsl:variable name="vnewvars">         <xsl:sequence select="$pvars"/>         <var name="{$vcurrentfor/@var}" value="{current()}"/>       </xsl:variable>      <xsl:apply-templates select="$vcurrentfor/node()">       <xsl:with-param name="pvars" select="$vnewvars/*"/>     </xsl:apply-templates>   </xsl:for-each>  </xsl:template>   <xsl:template match="text()[contains(., '$(')]">   <xsl:param name="pvars" as="element()*"/>    <xsl:analyze-string select="."     regex="\$\((.+?)\)">      <xsl:non-matching-substring>        <xsl:value-of select="."/>      </xsl:non-matching-substring>      <xsl:matching-substring>        <xsl:variable name="vname" select="regex-group(1)"/>         <xsl:variable name="vreplacement" select=         "$pvars[@name eq $vname][last()]/@value"/>        <xsl:sequence select="string($vreplacement)"/>      </xsl:matching-substring>   </xsl:analyze-string>  </xsl:template> </xsl:stylesheet> 

when transformation applied on provided xml document:

<doc>     <stuff>         <for var="i" from="1" to="2">             <item>$(i)</item>             <for var="j" from="2" to="4">                 <inneritem>$(j)</inneritem>             </for>         </for>     </stuff> </doc> 

the wanted, correct result produced:

<doc>    <stuff>       <item>1</item>       <inneritem>2</inneritem>       <inneritem>3</inneritem>       <inneritem>4</inneritem>       <item>2</item>       <inneritem>2</inneritem>       <inneritem>3</inneritem>       <inneritem>4</inneritem>    </stuff> </doc> 

it possible perform more complicated processing:

using variables differnt levels:

<doc>     <stuff>         <for var="x" from="1" to="2">             <item>$(x)</item>             <for var="y" from="2" to="4">                 <inneritem>$(x).$(y)</inneritem>             </for>         </for>     </stuff> </doc> 

the result on document is:

<doc>    <stuff>       <item>1</item>       <inneritem>1.2</inneritem>       <inneritem>1.3</inneritem>       <inneritem>1.4</inneritem>       <item>2</item>       <inneritem>2.2</inneritem>       <inneritem>2.3</inneritem>       <inneritem>2.4</inneritem>    </stuff> </doc> 

or xml document:

<doc>     <stuff>         <for var="x" from="1" to="2">           <item>              <value>$(x)</value>                 <for var="y" from="2" to="4">                     <inneritem>                       <value>$(x).$(y)</value>                         <for var="z" from="3" to="5">                           <inner-most-item>$(x).$(y).$(z)</inner-most-item>                         </for>                     </inneritem>                 </for>           </item>         </for>     </stuff> </doc> 

the result is:

<doc>    <stuff>       <item>          <value>1</value>          <inneritem>             <value>1.2</value>             <inner-most-item>1.2.3</inner-most-item>             <inner-most-item>1.2.4</inner-most-item>             <inner-most-item>1.2.5</inner-most-item>          </inneritem>          <inneritem>             <value>1.3</value>             <inner-most-item>1.3.3</inner-most-item>             <inner-most-item>1.3.4</inner-most-item>             <inner-most-item>1.3.5</inner-most-item>          </inneritem>          <inneritem>             <value>1.4</value>             <inner-most-item>1.4.3</inner-most-item>             <inner-most-item>1.4.4</inner-most-item>             <inner-most-item>1.4.5</inner-most-item>          </inneritem>       </item>       <item>          <value>2</value>          <inneritem>             <value>2.2</value>             <inner-most-item>2.2.3</inner-most-item>             <inner-most-item>2.2.4</inner-most-item>             <inner-most-item>2.2.5</inner-most-item>          </inneritem>          <inneritem>             <value>2.3</value>             <inner-most-item>2.3.3</inner-most-item>             <inner-most-item>2.3.4</inner-most-item>             <inner-most-item>2.3.5</inner-most-item>          </inneritem>          <inneritem>             <value>2.4</value>             <inner-most-item>2.4.3</inner-most-item>             <inner-most-item>2.4.4</inner-most-item>             <inner-most-item>2.4.5</inner-most-item>          </inneritem>       </item>    </stuff> </doc> 

i stop here, given design of language, possibilities limitless.

update: op has asked:

"is there way allow expansion inside attributes? example: <inner-most-item id="$(i)"> "

yes, quite easy -- adding new template matching attributes , refactoring code:

<xsl:stylesheet version="2.0"      xmlns:xsl="http://www.w3.org/1999/xsl/transform"      xmlns:my="my:my">      <xsl:output omit-xml-declaration="yes" indent="yes"/>      <xsl:strip-space elements="*"/>       <xsl:template match="node()|@*">       <xsl:param name="pvars" as="element()*"/>       <xsl:copy>        <xsl:apply-templates select="node()|@*">          <xsl:with-param name="pvars" select="$pvars"/>        </xsl:apply-templates>       </xsl:copy>      </xsl:template>       <xsl:template match="for">       <xsl:param name="pvars" as="element()*"/>        <xsl:variable name="vcurrentfor" select="."/>        <xsl:for-each select="@from @to">           <xsl:variable name="vnewvars">             <xsl:sequence select="$pvars"/>             <var name="{$vcurrentfor/@var}" value="{current()}"/>           </xsl:variable>          <xsl:apply-templates select="$vcurrentfor/node()">           <xsl:with-param name="pvars" select="$vnewvars/*"/>         </xsl:apply-templates>       </xsl:for-each>      </xsl:template>       <xsl:template match="text()[contains(., '$(')]">       <xsl:param name="pvars" as="element()*"/>        <xsl:value-of select="my:evaltext($pvars, .)"/>      </xsl:template>       <xsl:template match="@*[contains(., '$(')]">       <xsl:param name="pvars" as="element()*"/>        <xsl:attribute name="{name()}">         <xsl:value-of select="my:evaltext($pvars, .)"/>       </xsl:attribute>       </xsl:template>       <xsl:function name="my:evaltext">       <xsl:param name="pvars" as="element()*"/>        <xsl:param name="ptext"/>            <xsl:analyze-string select="$ptext"             regex="\$\((.+?)\)">              <xsl:non-matching-substring>                <xsl:value-of select="."/>              </xsl:non-matching-substring>              <xsl:matching-substring>                <xsl:variable name="vname" select="regex-group(1)"/>                 <xsl:variable name="vreplacement" select=                 "$pvars[@name eq $vname][last()]/@value"/>                <xsl:value-of select="string($vreplacement)"/>              </xsl:matching-substring>           </xsl:analyze-string>      </xsl:function> </xsl:stylesheet> 

when transformation applied on following xml document:

<doc>     <stuff>         <for var="i" from="1" to="2">             <item name="x$(i)">$(i)</item>             <for var="j" from="2" to="4">                 <inneritem name="x$(i).$(j)">$(j)</inneritem>             </for>         </for>     </stuff> </doc> 

the wanted, correct result produced:

<doc>    <stuff>       <item name="x1">1</item>       <inneritem name="x1.2">2</inneritem>       <inneritem name="x1.3">3</inneritem>       <inneritem name="x1.4">4</inneritem>       <item name="x2">2</item>       <inneritem name="x2.2">2</inneritem>       <inneritem name="x2.3">3</inneritem>       <inneritem name="x2.4">4</inneritem>    </stuff> </doc> 

Comments

Popular posts from this blog

jquery - Invalid Assignment Left-Hand Side -

java - Play! framework 2.0: How to display multiple image? -

gmail - Is there any documentation for read-only access to the Google Contacts API? -