【问题标题】:XSL Embedded for-eachXSL Embedded for-each
【发布时间】:2011-10-30 01:11:52
【问题描述】:

我知道这在此处有所涉及,但我是 XSLT 的新手,我正在尝试在输入 XML 中位于不同位置但处于同一节点级别的节点上执行嵌套的 for-each 循环。

所以...例如:

    <Level1>
    <Level2>
        <Level3a>
          <Item1>Clothes Washed</Item1>
          <Item2>08/02/2011 06:54</Item2>
          <DoneBy>Ingrid</DoneBy>
        </Level3a>
        <Level3a>
          <Item1>Car Washed</Item1>
          <Item2>08/02/2011 08:25</Item2>
          <DoneBy>Jeanette</DoneBy>
        </Level3a>
        <Level3a>
          <Item1>Dog Walked</Item1>
          <Item2>08/02/2011 10:30</Item2>
          <DoneBy>Ingrid</DoneBy>
        </Level3a>
        <Level3b>
          <DoneWho>Ingrid</DoneWho>
          <JobTitle>Main Asst</JobTitle>
        </Level3b>
        <Level3b>
          <DoneWho>Jeanette</DoneWho>
          <JonTitle>2nd Asst</JobTitle>
        </Level3b>
      </Level2>
    </Level1>

我需要输出是

    <Jobs>
      <CompletedJob>
        <JobTitle>Main Asst</JobTitle>
        <Job>Clothes Washed</Job>
        <CompOn>08/02/2011</CompOn>
        <CompAt>06:54<CompAt>
      </CompletedJob>
      <CompletedJob>
        <JobTitle>Main Asst</JobTitle>
        <Job>Dog Walked</Job>
        <CompOn>08/02/2011</CompOn>
        <CompAt>10:30</CompAt>
      </CompletedJob>
      <CompletedJob>
        <JobTitle>2nd Asst</JobTitle>
        <Job>Car Washed</Job>
        <CompOn>08/02/2011</CompOn>
        <CompAt>08:25</CompAt>
      </CompletedJob>
    </Jobs>

编辑:请查看上述输出中的更改...我想这确实是我想要做的。 将输出替换为 ...再次感谢。

我尝试了嵌套 for-each 循环,但我无法从主 for-each 循环中引用不同的节点。

非常感谢任何帮助。

【问题讨论】:

  • 输入 xml 格式不正确,我想这是一个错字,不是问题的一部分 - 第 6 行
  • 又一个错字从底部数第 4 行:2nd Asst

标签: xml xslt loops foreach nested


【解决方案1】:

您不应该使用嵌套循环(或根本没有循环)。

使用输入 XML 的固定版本:

<Level1>
  <Level2>
    <Level3a>
      <Item1>Clothes Washed</Item1>
      <Item2>08/02/2011 06:54</Item2>
      <DoneBy>Ingrid</DoneBy>
    </Level3a>
    <Level3a>
      <Item1>Car Washed</Item1>
      <Item2>08/02/2011 08:25</Item2>
      <DoneBy>Jeanette</DoneBy>
    </Level3a>
    <Level3a>
      <Item1>Dog Walked</Item1>
      <Item2>08/02/2011 10:30</Item2>
      <DoneBy>Ingrid</DoneBy>
    </Level3a>
    <Level3b>
      <DoneWho>Ingrid</DoneWho>
      <JobTitle>Main Asst</JobTitle>
    </Level3b>
    <Level3b>
      <DoneWho>Jeanette</DoneWho>
      <JobTitle>2nd Asst</JobTitle>
    </Level3b>
  </Level2>
</Level1>

还有这个样式表:

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

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

  <xsl:template match="/Level1">
    <Jobs>
      <xsl:apply-templates select="*/Level3a">
        <xsl:sort select="DoneBy"/>
      </xsl:apply-templates>
    </Jobs>
  </xsl:template>

  <xsl:template match="Level3a">
    <CompletedJob>
      <Who><xsl:apply-templates select="DoneBy"/></Who>
      <Job><xsl:apply-templates select="Item1"/></Job>
      <!-- XSLT 2.0 option
      <CompOn><xsl:value-of select="tokenize(Item2,' ')[1]"></xsl:value-of></CompOn>
      <CompAt><xsl:value-of select="tokenize(Item2,' ')[2]"></xsl:value-of></CompAt>
      -->
      <CompOn><xsl:value-of select="substring-before(Item2, ' ')"></xsl:value-of></CompOn>
      <CompAt><xsl:value-of select="substring-after(Item2, ' ')"></xsl:value-of></CompAt>
    </CompletedJob>
  </xsl:template>

  <xsl:template match="DoneBy|Item1|Item2">
    <xsl:apply-templates/>
  </xsl:template>

</xsl:stylesheet>

产生想要的输出:

<Jobs>
   <CompletedJob>
      <Who>Ingrid</Who>
      <Job>Clothes Washed</Job>
      <CompOn>08/02/2011</CompOn>
      <CompAt>06:54</CompAt>
   </CompletedJob>
   <CompletedJob>
      <Who>Ingrid</Who>
      <Job>Dog Walked</Job>
      <CompOn>08/02/2011</CompOn>
      <CompAt>10:30</CompAt>
   </CompletedJob>
   <CompletedJob>
      <Who>Jeanette</Who>
      <Job>Car Washed</Job>
      <CompOn>08/02/2011</CompOn>
      <CompAt>08:25</CompAt>
   </CompletedJob>
</Jobs>

注意:到目前为止,我的答案是唯一一个对输出进行排序并产生您想要的确切结果(减去格式正确错误)的答案。

【讨论】:

    【解决方案2】:

    不需要嵌套循环 - XSLT 天生就足够递归。

    按照样式表完成工作:

    <?xml version="1.0"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
        <xsl:output method="xml"/>
        <xsl:template match="/">
            <Jobs>
                <xsl:for-each select="*/*/Level3a">
                    <xsl:sort select="DoneBy"/>
    
                    <CompletedJob>
                        <JobTitle>
                            <xsl:value-of select="../Level3b[DoneWho=current()/DoneBy]/JobTitle"/>
                        </JobTitle>
                        <Job>
                            <xsl:value-of select="Item1"/>
                        </Job>
                        <CompOn>
                            <xsl:value-of select="substring-before(Item2,' ')"/>
                        </CompOn>
                        <CompAt>
                            <xsl:value-of select="substring-after(Item2,' ')"/>
                        </CompAt>
                    </CompletedJob>
    
                </xsl:for-each>
             </Jobs>
        </xsl:template>
    
    </xsl:stylesheet>
    

    Level3b 元素现在用于将 DoneBy 解析为 JobTitle。

    【讨论】:

    • 这似乎是一个很好的解决方案,但在我的实际 XML / XSLT 的更宏大的方案中,我没有让它工作......我有模板 match="/" 在在我的主要 XML 标记之前开始......所以它不会让我将其他模板放入该模板中。
    • 好吧,如果我理解你的话,你需要避免使用其他模板...我将 xsl 编辑为不使用任何模板,HTH
    • 太棒了!我确实想出了如何使用模板..它们基本上就像一个函数调用。
    • 除了调用是由 xslt 处理器调用的,而 for-each 是您自己的调用 - 这就是为什么它在 xslt 规范中称为 直接处理
    【解决方案3】:

    该死的,打败我。哦,好吧,不妨也给我的解决方案:

    <Level1>
        <Level2>
            <Level3>
                <Item1>Clothes Washed</Item1>
                <Item2>08/02/2011 06:54</Item2>
                <DoneBy>Ingrid</DoneBy>
            </Level3>
            <Level3>
                <Item1>Car Washed</Item1>
                <Item2>08/02/2011 08:25</Item2>
                <DoneBy>Jeanette</DoneBy>
            </Level3>
            <Level3>
                <Item1>Dog Walked</Item1>
                <Item2>08/02/2011 10:30</Item2>
                <DoneBy>Ingrid</DoneBy>
            </Level3>
        </Level2>
    </Level1>
    
    
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="/">
            <Jobs>
            <xsl:for-each select="/Level1/Level2/Level3">
                <CompletedJob>
                    <Who><xsl:value-of select="./DoneBy"/></Who>
                    <Job><xsl:value-of select="./Item1"/></Job>
                    <CompOn><xsl:value-of select="substring(./Item2, 1, 10)"/></CompOn>
                    <CompAt><xsl:value-of select="substring(./Item2, 12, 5)"/></CompAt>
                </CompletedJob>
            </xsl:for-each>
            </Jobs>
        </xsl:template>
    </xsl:stylesheet>
    

    【讨论】:

      【解决方案4】:

      你的 xml 输入真的很糟糕。

          <xsl:template match="/">
          <xsl:apply-templates select="//Item1"/>
      </xsl:template>
      
      <xsl:template match="Item1">
          <CompletedJob>
              <Who><xsl:value-of select="../DoneBy"/></Who>
              <Job><xsl:value-of select="."/></Job>
              <xsl:apply-templates select="../Item2"/>
          </CompletedJob>
      </xsl:template>
      
      <xsl:template match="Item2">
          <CompOn><xsl:value-of select="substring-before(text(), ' ')"/></CompOn>
          <CompAt><xsl:value-of select="substring-after(text(), ' ')"/></CompAt>
      </xsl:template>
      

      这会生成你的输出

      【讨论】:

      • 我很欣赏所有的解决方案。我还不太了解模板...它类似于编程中的函数吗?我还在学习,所以谢谢大家抽出时间来回答。
      • P.S.这不是我的输入。我只需要使用它来获得我需要的输出。我鄙视以这种方式移动数据。我更像是那种与源头联系在一起的人……我们只是还没有生活在那个世界里。哦,好吧,开始学习 XSL 模板。
      • 这是一本开始学习 XSL 的好书——Michael Key 的 XSLT 程序员参考。有一部分解释了如何不使用编程模式 =)
      猜你喜欢
      • 2012-10-11
      • 1970-01-01
      • 2016-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-09
      • 1970-01-01
      • 2023-01-11
      相关资源
      最近更新 更多