XSLT计数器递归和传递参数

时间:2017-01-17 22:29:23

标签: xslt recursion parameters counter

我已经能够创建一个XSLT(包含)来将源XML转换为目标所需的XML格式(包括在内),但我确信这可以利用一些超出我熟练程度的内置XSLT功能进行优化。感谢任何想法。

核心要求是输入XML包含标题值和数据(第一个记录包含标题/标签,后续记录包含相应标签的数据,因此输出数据在位置上与第一个记录匹配),OutPut XML包含Name值对,名称描述数据元素(来自源中第一个记录的标题/标签),元素是" labelXX"," valueXX",因此需要计数器增加XX 。输出还包含一个标题记录,它基本上只包含元素的名称,其余部分来自输出。

源XML

<?xml version="1.0" encoding="UTF-8"?>
<SpecExportFF>
 <recordset>
<value>12345</value>
<value>xyz</value>
    <value>a0</value>
    <value>b0</value>
    <value>c0</value>
    <value>d0</value>
    <value>e0</value>
    <value>f0</value>
    <value>g0</value>
    <value>h0</value>
    <value>i0</value>
    <value>j0</value>
    <value>k0</value>
    <value>l0</value>
    <value>m0</value>
    <value>n0</value>
    <value>o0</value>
    <value>p0</value>
    <value>q0</value>
    <value>r0</value>
    <value>s0</value>
    <value>t0</value>
    <value>a1</value>
    <value>b1</value>
    <value>c1</value>
    <value>d1</value>
    <value>e1</value>
    <value>f1</value>
    <value>g1</value>
    <value>h1</value>
    <value>i1</value>
    <value>j1</value>
    <value>k1</value>
    <value>l1</value>
    <value>m1</value>
    <value>n1</value>
    <value>o1</value>
    <value>p1</value>
    <value>q1</value>
    <value>r1</value>
    <value>s1</value>
    <value>t1</value>
    <value>a2</value>
    <value>b2</value>
    <value>c2</value>
    <value>d2</value>
    <value>e2</value>
    <value>f2</value>
    <value>g2</value>
    <value>h2</value>
    <value>i2</value>
    <value>j2</value>
    <value>k2</value>
    <value>l2</value>
    <value>m2</value>
    <value>n2</value>
    <value>o2</value>
    <value>p2</value>
    <value>q2</value>
    <value>r2</value>
    <value>s2</value>
    <value>t2</value>
 </recordset>
 <recordset>
    <value>A12345</value>
    <value>snacks</value>
    <value>1</value>
    <value>cereals</value>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
 </recordset>
 <recordset>
    <value>A245678</value>
    <value>snacks</value>
    <value>1</value>
    <value>chips</value>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value>potato</value>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value>fried</value>
    <value/>
    <value></value>
    <value/>
    <value>baked</value>
    <value></value>
    <value></value>
    <value></value>
    <value/>
    <value></value>
    <value/>
    <value>20g</value>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value/>
    <value></value>
 </recordset>
</SpecExportFF>

输出目标XML     

    <?xml version="1.0" encoding="UTF-8"?>
    <SpecExportOutputFF>
    <header>
       <item>item</item>
       <category>category</category>
       <label01>label01</label01>
       <spec01>spec01</spec01>
       <label02>label02</label02>
       <spec02>spec02</spec02>
       <label03>label03</label03>
       <spec03>spec03</spec03>
       <label04>label04</label04>
       <spec04>spec04</spec04>
       <label05>label05</label05>
       <spec05>spec05</spec05>
       <label06>label06</label06>
       <spec06>spec06</spec06>
       <label07>label07</label07>
       <spec07>spec07</spec07>
       <label08>label08</label08>
       <spec08>spec08</spec08>
       <label09>label09</label09>
       <spec09>spec09</spec09>
       <label10>label10</label10>
       <spec10>spec10</spec10>
       <label11>label11</label11>
       <spec11>spec11</spec11>
       <label12>label12</label12>
       <spec12>spec12</spec12>
       <label13>label13</label13>
       <spec13>spec13</spec13>
       <label14>label14</label14>
       <spec14>spec14</spec14>
       <label15>label15</label15>
       <spec15>spec15</spec15>
       <label16>label16</label16>
       <spec16>spec16</spec16>
       <label17>label17</label17>
       <spec17>spec17</spec17>
       <label18>label18</label18>
       <spec18>spec18</spec18>
       <label19>label19</label19>
       <spec19>spec19</spec19>
       <label20>label20</label20>
       <spec20>spec20</spec20>
       <label21>label21</label21>
       <spec21>spec21</spec21>
       <label22>label22</label22>
       <spec22>spec22</spec22>
       <label23>label23</label23>
       <spec23>spec23</spec23>
       <label24>label24</label24>
       <spec24>spec24</spec24>
       <label25>label25</label25>
       <spec25>spec25</spec25>
       <label26>label26</label26>
       <spec26>spec26</spec26>
       <label27>label27</label27>
       <spec27>spec27</spec27>
       <label28>label28</label28>
       <spec28>spec28</spec28>
       <label29>label29</label29>
       <spec29>spec29</spec29>
       <label30>label30</label30>
       <spec30>spec30</spec30>
       <label31>label31</label31>
       <spec31>spec31</spec31>
       <label32>label32</label32>
       <spec32>spec32</spec32>
       <label33>label33</label33>
       <spec33>spec33</spec33>
       <label34>label34</label34>
       <spec34>spec34</spec34>
       <label35>label35</label35>
       <spec35>spec35</spec35>
       <label36>label36</label36>
       <spec36>spec36</spec36>
       <label37>label37</label37>
       <spec37>spec37</spec37>
       <label38>label38</label38>
       <spec38>spec38</spec38>
       <label39>label39</label39>
       <spec39>spec39</spec39>
       <label40>label40</label40>
       <spec40>spec40</spec40>
       <label41>label41</label41>
       <spec41>spec41</spec41>
       <label42>label42</label42>
       <spec42>spec42</spec42>
       <label43>label43</label43>
       <spec43>spec43</spec43>
       <label44>label44</label44>
       <spec44>spec44</spec44>
       <label45>label45</label45>
       <spec45>spec45</spec45>
       <label46>label46</label46>
       <spec46>spec46</spec46>
       <label47>label47</label47>
       <spec47>spec47</spec47>
       <label48>label48</label48>
       <spec48>spec48</spec48>
       <label49>label49</label49>
       <spec49>spec49</spec49>
       <label50>label50</label50>
       <spec50>spec50</spec50>
       <label51>label51</label51>
       <spec51>spec51</spec51>
       <label52>label52</label52>
       <spec52>spec52</spec52>
       <label53>label53</label53>
       <spec53>spec53</spec53>
       <label54>label54</label54>
       <spec54>spec54</spec54>
       <label55>label55</label55>
       <spec55>spec55</spec55>
       <label56>label56</label56>
       <spec56>spec56</spec56>
       <label57>label57</label57>
       <spec57>spec57</spec57>
       <label58>label58</label58>
       <spec58>spec58</spec58>
       <label59>label59</label59>
       <spec59>spec59</spec59>
       <label60>label60</label60>
       <spec60>spec60</spec60>
    </header>
    <data>
       <item>A12345</item>
       <category>snacks</category>
       <label01>a0</label01>
       <spec01>1</spec01>
       <label02>b0</label02>
       <spec02>cereals</spec02>
    </data>
    <data>
       <item>A245678</item>
       <category>snacks</category>
       <label01>a0</label01>
       <spec01>1</spec01>
       <label02>b0</label02>
       <spec02>chips</spec02>
       <label03>q0</label03>
       <spec03>potato</spec03>
       <label04>e1</label04>
       <spec04>fried</spec04>
       <label05>i1</label05>
       <spec05>baked</spec05>
       <label06>p1</label06>
       <spec06>20g</spec06>
    </data>
    </SpecExportOutputFF>
    
我的XSLT(它可以工作,但在我看来太复杂了,想要优化它)

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/SpecExportFF">
        <SpecExportOutputFF>
            <xsl:for-each select="recordset">
                <xsl:variable name="IndexOfCurrentRecordset">
                    <xsl:value-of select="position()"/>
                </xsl:variable>
                <xsl:if test="$IndexOfCurrentRecordset = 1">
                    <header>
                        <xsl:for-each select="value">
                            <xsl:variable name="IndexOfCurrentNode" select="position()"/>
                            <xsl:variable name="ElementSuffix" select="format-number(($IndexOfCurrentNode - 2),'00')"/>
                            <!--<hello1><xsl:value-of select="$IndexOfCurrentNode"/></hello1>-->
                            <xsl:choose>
                                <xsl:when test="$IndexOfCurrentNode = 1">
                                    <xsl:element name="item">item</xsl:element>
                                </xsl:when>
                                <xsl:when test="$IndexOfCurrentNode = 2">
                                    <xsl:element name="category">category</xsl:element>
                                </xsl:when>
                                <xsl:when test="($IndexOfCurrentNode > 2)">
                                    <xsl:element name="{concat('label',$ElementSuffix)}">
                                        <xsl:value-of select="concat('label',$ElementSuffix)"/>
                                    </xsl:element>
                                    <xsl:element name="{concat('spec',$ElementSuffix)}">
                                        <xsl:value-of select="concat('spec',$ElementSuffix)"/>
                                    </xsl:element>
                                </xsl:when>
                                <xsl:otherwise>
                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:for-each>
                    </header>
                </xsl:if>
                <xsl:if test="$IndexOfCurrentRecordset > 1">
                    <data>
                        <xsl:call-template name="iterate_values">
                            <xsl:with-param name="i" select="1"/>
                            <xsl:with-param name="currentNode" select="./value[1]"/>
                            <xsl:with-param name="IndexOfCurrentRecordset" select="$IndexOfCurrentRecordset"/>
                            <xsl:with-param name="IndexOfCurrentNode" select="1"/>
                        </xsl:call-template>
                    </data>
                </xsl:if>
            </xsl:for-each>
        </SpecExportOutputFF>
    </xsl:template>

    <xsl:template name="iterate_values">
        <xsl:param name="i"/>
        <xsl:param name="currentNode"/>
        <xsl:param name="IndexOfCurrentRecordset"/>
        <xsl:param name="IndexOfCurrentNode"/>

        <xsl:variable name="ElementSuffix" select="format-number($i,'00')"/>

        <xsl:choose>
            <xsl:when test="$IndexOfCurrentNode = 1">
                <xsl:element name="item">
                    <xsl:value-of select="$currentNode/text()"/>
                </xsl:element>
                <xsl:if test="/SpecExportFF/recordset[position() = $IndexOfCurrentRecordset]/value[position() = $IndexOfCurrentNode+1]">
                    <xsl:call-template name="iterate_values">
                        <xsl:with-param name="i" select="$i"/>
                        <xsl:with-param name="currentNode" select="/SpecExportFF/recordset[position() = $IndexOfCurrentRecordset]/value[position() = $IndexOfCurrentNode+1]"/>
                        <xsl:with-param name="IndexOfCurrentRecordset" select="$IndexOfCurrentRecordset"/>
                            <xsl:with-param name="IndexOfCurrentNode" select="$IndexOfCurrentNode+1"/>
                    </xsl:call-template>
                </xsl:if>
            </xsl:when>
            <xsl:when test="$IndexOfCurrentNode = 2">
                <xsl:element name="category">
                    <xsl:value-of select="$currentNode/text()"/>
                </xsl:element>
                <xsl:if test="/SpecExportFF/recordset[position() = $IndexOfCurrentRecordset]/value[position() = $IndexOfCurrentNode+1]">
                    <xsl:call-template name="iterate_values">
                        <xsl:with-param name="i" select="$i"/>
                        <xsl:with-param name="currentNode" select="/SpecExportFF/recordset[position() = $IndexOfCurrentRecordset]/value[position() = $IndexOfCurrentNode+1]"/>
                        <xsl:with-param name="IndexOfCurrentRecordset" select="$IndexOfCurrentRecordset"/>
                            <xsl:with-param name="IndexOfCurrentNode" select="$IndexOfCurrentNode+1"/>
                    </xsl:call-template>
                </xsl:if>
            </xsl:when>
            <xsl:when test="$IndexOfCurrentNode > 2 and $currentNode/text() != ''">
                <xsl:element name="{concat('label',$ElementSuffix)}">
                    <xsl:value-of select="/*/*[1]/value[$IndexOfCurrentNode]"/>
                </xsl:element>
                <xsl:element name="{concat('spec',$ElementSuffix)}">
                    <xsl:value-of select="$currentNode/text()"/>
                </xsl:element>
                <xsl:if test="/SpecExportFF/recordset[position() = $IndexOfCurrentRecordset]/value[position() = $IndexOfCurrentNode+1]">
                    <xsl:call-template name="iterate_values">
                        <xsl:with-param name="i" select="$i+1"/>
                        <xsl:with-param name="currentNode" select="/SpecExportFF/recordset[position() = $IndexOfCurrentRecordset]/value[position() = $IndexOfCurrentNode+1]"/>
                        <xsl:with-param name="IndexOfCurrentRecordset" select="$IndexOfCurrentRecordset"/>
                            <xsl:with-param name="IndexOfCurrentNode" select="$IndexOfCurrentNode+1"/>
                    </xsl:call-template>
                </xsl:if>
            </xsl:when>
            <xsl:otherwise>
                <xsl:if test="/SpecExportFF/recordset[position() = $IndexOfCurrentRecordset]/value[position() = $IndexOfCurrentNode+1]">
                    <xsl:call-template name="iterate_values">
                        <xsl:with-param name="i" select="$i"/>
                        <xsl:with-param name="currentNode" select="/SpecExportFF/recordset[position() = $IndexOfCurrentRecordset]/value[position() = $IndexOfCurrentNode+1]"/>
                        <xsl:with-param name="IndexOfCurrentRecordset" select="$IndexOfCurrentRecordset"/>
                            <xsl:with-param name="IndexOfCurrentNode" select="$IndexOfCurrentNode+1"/>
                    </xsl:call-template>
                </xsl:if>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

1 个答案:

答案 0 :(得分:0)

只是为了挑战,我找出了你想要做的事情。我认为主要的问题是你正在考虑当务之急,但需要改变思维方式,因为XSLT是一种功能性语言,而且情况非常不同。

确实,这就是我想出来的......

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes" />

  <xsl:variable name="labels" select="/SpecExportFF/recordset[1]/value[position() > 2]" />

  <xsl:template match="SpecExportFF">
    <SpecExportOutputFF>
       <xsl:apply-templates select="recordset" />
    </SpecExportOutputFF>
  </xsl:template>

  <xsl:template match="recordset[1]">
    <header>
     <item>item</item>
     <category>category</category>
     <xsl:for-each select="value">
     </xsl:for-each> 
    </header>
  </xsl:template>

  <xsl:template match="recordset[1]">
    <header>
     <item>item</item>
     <category>category</category>
     <xsl:for-each select="value[position() > 2]">
       <xsl:variable name="ElementSuffix" select="format-number(position(),'00')"/>
       <xsl:element name="{concat('label',$ElementSuffix)}">
         <xsl:value-of select="concat('label',$ElementSuffix)"/>
       </xsl:element>
       <xsl:element name="{concat('spec',$ElementSuffix)}">
          <xsl:value-of select="concat('spec',$ElementSuffix)"/>
       </xsl:element>
     </xsl:for-each> 
    </header>
  </xsl:template>

  <xsl:template match="recordset">
     <data>
       <item><xsl:value-of select="value[1]" /></item>
       <category><xsl:value-of select="value[2]" /></category>
       <xsl:for-each select="value[position() > 2][normalize-space()]">
            <xsl:variable name="ElementSuffix" select="format-number(position(),'00')"/>
            <xsl:variable name="LabelPos" select="count(preceding-sibling::value) - 1" />
            <xsl:element name="{concat('label',$ElementSuffix)}">
              <xsl:value-of select="$labels[position() = $LabelPos]" />
            </xsl:element>
            <xsl:element name="{concat('spec',$ElementSuffix)}">
               <xsl:value-of select="."/>
            </xsl:element>
       </xsl:for-each>
    </data>
  </xsl:template>
</xsl:stylesheet>