【问题标题】:To Make My Outer Loop be Aware of Existence of the Inner Loop让我的外循环知道内循环的存在
【发布时间】:2011-02-25 16:56:23
【问题描述】:

我认为我的标题基本上描述了我现在在 XSLT 中遇到的问题。但更准确地说,我的意思是“如何让外部循环知道内部循环是否在 XSLT 中执行”。请原谅我,因为我是这个领域的初学者。

首先,我的初始 xml 是空白的,我的 XSLT 基本上是从大量 xml 数据文件中获取数据,并创建一个带有我想要的标签的结果 xml 文件。具体来说,我所关心的问题发生在“Outer”和“Inner”两个模板中,这两个模板实际上本身就是循环结构,它们试图循环遍历某些xml文件的每一行。流程的控制很简单:我在我的主 XSLT 中调用 Outer,然后我们进入 Outer 的“for-each”循环,在这个 Outer 的每次运行中,我将调用“Inner”,所以它是一个循环-within-Loop 结构。

现在,我们可以忽略所有其他事情,但只知道一旦我进入外循环,我将创建一个“HL”段,然后调用 Inner 并进入内循环,并且对于 Inner 的每次运行,我也会创建一个“HL”段。需要注意的一点是,除非满足某些条件,否则无法进入 Inner,因此可能存在根本不进入 Inner 的情况,并且所有 HL 段都是在 Outer 中创建的。

我要算的就是这个HL段,我分享一小段代码让大家有个思路。

外环:

<xsl:template name="Outer">
  <xsl:for-each select="$rechca//d_claim_rechca_export_xml_row">
    <xsl:variable name="rechca_claim_no" select=claim_number/>
       <HL01>
         <xsl:if test="$indicator=0">                                    

              <xsl:value-of select="position()+1>
          </xsl:if>
       </HL01>
    ....

  <xsl:for-each select="$rechcf//d_claim_rechcf_export_xml_row">
   <xsl:if test="claim_number=$rechca_claim_number">
     <xsl:call-template name="Inner">
      <xsl:with-param name="HLcounter" select="position()+1"/>
      </xsl:call-template>
   </xsl:if> 
  </xsl:for-each>

 </xsl:for-each> 
</xsl:template>

内循环:

  <xsl:for-each select="$rechcf//d_claim_rechcf_export_xml_row[claim_number =$claimno and sequence_number = 1]">
 <Inner>
 <HL>
   <HL01>  
      <xsl:number value ="position()+$Hcount"/>
    </HL01>
    ....

目前,$rechca 中有两行,$rechcf 中只有一行满足这两个循环,所以 Outer Loop 重复了两次,Inner 只执行了一次。因此,期望的输出应该是

<Outer>  
  <HL>
    <HL01>2</HL01>
  </HL>
     <Inner>
         <HL01>3</HL01>     
     </Inner>
  <HL>
    <HL01>4</HL01>
  </HL>
 </Outer>

但当前的输出是:

 <Outer>  
  <HL>
    <HL01>2</HL01>
  </HL>
     <Inner>
         <HL01>3</HL01>     
     </Inner>
  <HL>
    <HL01>3</HL01>
  </HL>
 </Outer>

如果没有这个内部循环,事情就完美了。但是目前Outer Loop并不知道Inner Loop的存在:一旦Inner被调用,那么当它把控制权返回给outer Loop时,顶部的不应该包含“position()+1”而是“position( )+2" 代替(因为在进入这个新的 Outer 之前 Inner 被调用了一次,所以在那里创建了一个新的 HL 段)。如您所见,我创建了一个名为“indicator”的变量。在 Java/C++ 中,我只需将此指标更新为 0 以外的另一个值,每次输入内部时,将其设置为 1。然后在 Outer 的 HL01 进行测试,以确定要添加到 position() 的正确值。

真的卡在这里,请帮忙!谢谢。

【问题讨论】:

  • @Robert:只需提供您的源 XML 文档(尽可能少)和想要的结果,并解释必须遵守的任何规则——然后我们将向您展示一个解决方案。您的主要问题是,您作为命令式语言程序员正在思考,并且您正在尝试实现不可能的解决方案,甚至不知道如何找到更简单的功能解决方案。
  • @Dimitre:问题是,代码真的很长。请问可以发邮件给你吗?
  • @Robert:我们不需要“代码”。我们需要一个简单的 XML 文档和想要的结果,以及对问题的简短定义(规则、约束等)。没有人会“纠正”在功能设置中根本无法纠正的命令式代码。
  • @Dimitre:希望现在更好。
  • @Robert:不完全是:不清楚内循环后的输出是如何产生的——这在您显示的代码中不存在。正如我之前所说,我们根本没有也不应该讨论你的代码——尤其是当你没有解释它应该做什么的时候。

标签: xml xslt loops


【解决方案1】:

无需解释命令式和声明式范式之间差异的最简单的解决方案是两阶段转换。这个样式表:

<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:template match="/">
        <xsl:variable name="vRTF-FirstPass">
            <Outer>
                <HL>
                    <HL01/>
                </HL>
                <Inner>
                    <HL01/>
                </Inner>
                <HL>
                    <HL01/>
                </HL>
            </Outer>
        </xsl:variable>
        <xsl:apply-templates
             select="msxsl:node-set($vRTF-FirstPass)/node()"
             mode="second-pass"/>
    </xsl:template>
    <xsl:template match="node()|@*" mode="second-pass">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" mode="second-pass"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="HL01" mode="second-pass">
        <xsl:copy>
            <xsl:variable name="vPosition">
                <xsl:number level="any"/>
            </xsl:variable>
            <xsl:value-of select="$vPosition + 1"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

任意输入(未使用),输出:

<Outer>
    <HL>
        <HL01>1</HL01>
    </HL>
    <Inner>
        <HL01>2</HL01>
    </Inner>
    <HL>
        <HL01>3</HL01>
    </HL>
</Outer>

注意node-set() 扩展函数的使用(为您的 XSLT 处理器实现寻找正确的命名空间 URI,或者只是将您的环境升级到 XSLT 2.0 而忘记node-set())。模式以防万一。当然,您应该在 $vRTF-FirstPass 变量中输出所有您想要的输出,无论是通过 apply-templatescall-template 还是...等等。

编辑:您可以将“HL01”规则替换为其他类似但较短的内容模板:

<xsl:copy>
    <xsl:value-of select="count(preceding::HL01) + 2"/>
</xsl:copy>

【讨论】:

  • @Alejandro:效果很好。我认为两阶段翻译是计算 xslt 中段数的好方法。
  • @Robert:我很高兴它有帮助。正确地说,这是(f o g)(x) = f( g(x) ) 中的函数组合。这不是一种解决方法,而是声明性范例中的一种基本模式。
猜你喜欢
  • 1970-01-01
  • 2018-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-31
相关资源
最近更新 更多