【问题标题】:XSLT foreach loopXSLT foreach 循环
【发布时间】:2012-06-07 11:06:25
【问题描述】:

最近几周我才开始接触xslt和xml,我迫切需要一些帮助来编写一些代码来实现我想要实现的目标。 我有以下 xml:

<?xml version="1.0" encoding="UTF-8"?>
<abc1 formName="Form">
    <Level1>
        <Element1>ZZZ</Element1>
        <Element2>
            <SubElement1>Apples</SubElement1>
            <SubElement2>Oranges</SubElement2>
            <SubElement3>Pears</SubElement3>
            <SubElement4>Blueberries</SubElement4>
            <SubElement5>Milkshakes</SubElement5>
        </Element2>
    </Level1>
    <Level1>
        <Element1>XXX</Element1>
        <Element2>
            <SubElement1>Apples</SubElement1>
            <SubElement2>Oranges</SubElement2>
            <SubElement3>Kiwifruit</SubElement3>
            <SubElement4>Blueberries</SubElement4>
            <SubElement5>Soda</SubElement5>
        </Element2>
    </Level1>
</abc1>

以及下面的html表格:

<table>
<tr> 
    <td width="180" > Row 1</td>
    <td width="540" colspan="4"> Cell_A</td>
</tr>
<tr> 
    <td width="180" >Types</td>
    <td width="180" >Type 1</td>
    <td width="180" >Type 2</td>
    <td width="180" >Type 3</td>
</tr>
<tr> 
    <td width="180" >Row 2</td>
    <td width="180" >Cell_B</td>
    <td width="180" >Cell_C</td>
    <td width="180" >Cell_D</td>
</tr>
<tr> 
    <td width="180" > Row 3</td>
    <td width="180" >Cell_E</td>
    <td width="180" >Cell_F</td>
    <td width="180" >Cell_G</td>
</tr>
<tr> 
    <td width="180" >Row 4</td>
    <td width="180" >Cell_H</td>
    <td width="180" >Cell_I</td>
    <td width="180" >Cell_J</td>
</tr>
<tr> 
    <td width="180" > Row 5</td>
    <td width="540" colspan="4"> Cell_K</td>
</tr>
</table>

我无法使用 xslt 将数据从 xml 提取到表中,因为我需要应用的规则使其非常复杂。以下是需要应用于我遇到问题的单元格的规则。

(1)如果&lt;Element1&gt;的值在整个xml中都是一样的,那么:

如果 &lt;Element1&gt; = 'ZZZ' 那么 Cell_B = '10',Cell_C = '20',并且 Cell_D = '30'

如果 &lt;Element1&gt; = 'XXX' 那么 Cell_B = '100',Cell_C = '90',并且 Cell_D = '80'

但如果 &lt;Element1&gt; 值在 xml 中不同,则:

Cell_B = '10,100'、Cell_C = '20,90' 和 Cell_D = '30,80'

(2)如果&lt;SubElement5&gt;的值在整个xml中都是一样的,那么:

Cell_J = &lt;SubElement5&gt;的值

但是如果&lt;SubElement5&gt;在xml中的值不同,那么:

Cell_J = 两个 &lt;SubElement5&gt; 值的值,用逗号分隔

所以在这种情况下,Cell_J 的值将是“奶昔,苏打水”。

我一直在尝试不同的东西:

<xsl:for-each select="./Level1/Element2">
<xsl:value-of select="./SubElement5"/>
</xsl:for-each>

但我无法确定可以使用哪些代码来检查它们是否相同,因为我无法覆盖变量的值。

编辑: 请注意,我在上面指出的单元格(单元格 b、c、d 和 j)是我唯一的 需要帮助。此外,对于Element1,我可以有四个潜在值 遇到:ZZZ、XXX、AAA、BBB。其中每个所需的值将是: (单元格 b、c 和 d)

ZZZ:10,20,30
XXX:100,90,80
AAA:40,30,30
BBB:50,30,20

因此,如果整个 xml 中只有一个潜在值,则单元格应显示为上述值。如果两个元素 1 的值不同,则单元格应列出每个单元格中的上述值,用逗号分隔。

关于cell_j,我会试着解释一下。

首先,我需要确定&lt;SubElement5&gt; 在整个xml 中是否是相同的值。在这种情况下,它不是,在一个部分是奶昔,在另一个部分是“苏打水”。因此,cell_J 应该包含文本“Milkshakes, Soda”。

如果 xml 看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
    <abc1 formName="Form">
        <Level1>
            <Element1>ZZZ</Element1>
            <Element2>
                <SubElement1>Apples</SubElement1>
                <SubElement2>Oranges</SubElement2>
                <SubElement3>Pears</SubElement3>
                <SubElement4>Blueberries</SubElement4>
                <SubElement5>Milkshakes</SubElement5>
            </Element2>
        </Level1>
        <Level1>
            <Element1>XXX</Element1>
            <Element2>
                <SubElement1>Apples</SubElement1>
                <SubElement2>Oranges</SubElement2>
                <SubElement3>Kiwifruit</SubElement3>
                <SubElement4>Blueberries</SubElement4>
                <SubElement5>Milkshakes</SubElement5>
            </Element2>
        </Level1>
    </abc1>

那么 cell_j 的值就是“Milkshakes”。

提前感谢任何可以提供帮助的人。

回答这个问题:

在下面总结伍迪的所作所为,以供将来参考:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/abc1">
           <xsl:variable name="elements" select="//Element1[not(preceding::Element1 = .)]"/>
<table>
<tr> 
    <td width="180" > Row 1</td>
    <td width="540" colspan="4"> Cell_A</td>
</tr>
<tr> 
    <td width="180" >Types</td>
    <td width="180" >Type 1</td>
    <td width="180" >Type 2</td>
    <td width="180" >Type 3</td>
</tr>
<tr> 
    <td width="180" >Row 2</td>
    <td width="180" > <xsl:for-each select="$elements">
            <xsl:if test="position() != 1">,</xsl:if>
            <xsl:choose>
                <xsl:when test=". = 'AAA'">40</xsl:when>
                <xsl:when test=". = 'BBB'">50</xsl:when>
                <xsl:when test=". = 'XXX'">100</xsl:when>
                <xsl:when test=". = 'ZZZ'">10</xsl:when>
            </xsl:choose>
        </xsl:for-each></td>
    <td width="180" ><xsl:for-each select="$elements">
            <xsl:if test="position() != 1">,</xsl:if>
            <xsl:choose>
                <xsl:when test=". = 'AAA'">30</xsl:when>
                <xsl:when test=". = 'BBB'">30</xsl:when>
                <xsl:when test=". = 'XXX'">90</xsl:when>
                <xsl:when test=". = 'ZZZ'">20</xsl:when>
            </xsl:choose>
        </xsl:for-each></td>
    <td width="180" ><xsl:for-each select="$elements">
            <xsl:if test="position() != 1">,</xsl:if>
            <xsl:choose>
                <xsl:when test=". = 'AAA'">30</xsl:when>
                <xsl:when test=". = 'BBB'">20</xsl:when>
                <xsl:when test=". = 'XXX'">80</xsl:when>
                <xsl:when test=". = 'ZZZ'">30</xsl:when>
            </xsl:choose>
        </xsl:for-each></td>
</tr>
<tr> 
    <td width="180" > Row 3</td>
    <td width="180" >Cell_E</td>
    <td width="180" >Cell_F</td>
    <td width="180" >Cell_G</td>
</tr>
<tr> 
    <td width="180" >Row 4</td>
    <td width="180" >Cell_H</td>
    <td width="180" >Cell_I</td>
    <td width="180" ><xsl:for-each select="//SubElement5[not(preceding::SubElement5/text() = text())]">
       <xsl:if test="position() &gt; 1">, </xsl:if>
       <xsl:value-of select="."/>
    </xsl:for-each></td>
</tr>
<tr> 
    <td width="180" > Row 5</td>
    <td width="540" colspan="4"> Cell_K</td>
</tr>
</table>
        </xsl:template>
    </xsl:stylesheet>

伍迪,再次感谢你。这太棒了。

【问题讨论】:

  • 您忘记指定单元格 E、F、G、H 和 I 中的内容。请编辑问题并提供缺失的信息。

标签: xslt variables foreach


【解决方案1】:

你的问题的关键是你不能遍历数据来得到你想要的,因为你想要的答案是固定的,即,如果这个,输出那个,如果那个,输出另一个。因此,您需要一次完成每个部分。

是否只有两个 Level1?

        <xsl:template match="/abc1">
            <table>
                <tr> 
                    <td width="180" >Row 2</td>
            <xsl:choose>
                <xsl:when test="not(Level1/Element1='XXX')">
                    <!-- only ZZZ -->
                        <td width="180" >10</td>
                        <td width="180" >20</td>
                        <td width="180" >30</td>
                </xsl:when>
                <xsl:when test="not(Level1/Element1='ZZZ')">
                    <!-- only YYY -->
                        <td width="180" >100</td>
                        <td width="180" >90</td>
                        <td width="180" >80</td>   
                </xsl:when>
                <xsl:otherwise>
                    <!-- some combination -->
                        <td width="180" >10,100</td>
                        <td width="180" >20,90</td>
                        <td width="180" >30,80</td>
                </xsl:otherwise>
                </xsl:choose>
                </tr>
                .. continued
            </table>
        </xsl:template>
    </xsl:stylesheet>

以此类推。如果您有许多行,并且您希望它们用逗号分隔,那么您需要按部分执行 或者如果您必须为每个部分放置一个逗号列表。

抱歉,我看不到你想用 cell_j 做什么,而且似乎没有其他规则

编辑:但是,如果您有很多项目而不仅仅是 2 个,并且您需要一个逗号分隔的列表,您可以使用 xpath 来完成,所以:

<tr>
  <td>
    <xsl:for-each select="//SubElement5[not(preceding::SubElement5/text() = text())]">
       <xsl:if test="position() &gt; 1">, </xsl:if>
       <xsl:value-of select="."/>
    </xsl:for-each>
  </td>
  .. maybe other rows the same
</tr>

再次编辑:

因此,对于您更新的关于 cell_j 的问题,上面给出了正确的值。

对于您在第一部分中的更新,如果您希望显示所有值,则可以对同一主题进行变体(因此,如果您有所有 4 个选项,则有 4 个值)。不幸的是,由于您对每个都有固定的值,并且您必须逐个执行,因此您需要在很大的部分中执行此操作,因此在循环中:

<xsl:for-each select="//Element1[not(preceding::Element1/text() = text())]">

这将使您进入每个唯一条目的循环,然后根据该值是否为 XXX、ZZ​​Z 等来选择元素。

再次编辑

有几种方法可以实现你想要的第一部分,包括递归函数、外部文档和使用各种不同 XSLT 实现的节点集函数,但作为一种完全通用的易于查看的方法,这有点罗嗦,但易于查看的版本(我希望):

<xsl:variable name="elements" select="//Element1[not(preceding::Element1 = .)]"/>
<table>
    <tr> 
        <td width="180" >Row 2</td>
    <td>
        <xsl:for-each select="$elements">
            <xsl:if test="position() != 1">,</xsl:if>
            <xsl:choose>
                <xsl:when test=". = 'AAA'">40</xsl:when>
                <xsl:when test=". = 'BBB'">50</xsl:when>
                <xsl:when test=". = 'XXX'">100</xsl:when>
                <xsl:when test=". = 'ZZZ'">10</xsl:when>
            </xsl:choose>
        </xsl:for-each>
    </td>
    <td>
        <xsl:for-each select="$elements">
            <xsl:if test="position() != 1">,</xsl:if>
            <xsl:choose>
                <xsl:when test=". = 'AAA'">30</xsl:when>
                <xsl:when test=". = 'BBB'">30</xsl:when>
                <xsl:when test=". = 'XXX'">90</xsl:when>
                <xsl:when test=". = 'ZZZ'">20</xsl:when>
            </xsl:choose>
        </xsl:for-each>
    </td>
    <td>
        <xsl:for-each select="$elements">
            <xsl:if test="position() != 1">,</xsl:if>
            <xsl:choose>
                <xsl:when test=". = 'AAA'">30</xsl:when>
                <xsl:when test=". = 'BBB'">20</xsl:when>
                <xsl:when test=". = 'XXX'">80</xsl:when>
                <xsl:when test=". = 'ZZZ'">30</xsl:when>
            </xsl:choose>
        </xsl:for-each>
    </td>
    </tr>
 </table>   

实际上-我不确定对变量(在本例中为 $elements)的迭代是否是标准,它可能是 saxon 和 msxsl 的东西,但如果不是,您可以将其替换为值 ( //Element1[not(preceding::Element1 = .)])

【讨论】:

  • 感谢您的帮助。为了回答您的问题,我将在我的原始帖子中添加一些其他信息。
  • 再次感谢您。您能否通过示例扩展最后一节 ?我不太清楚你的意思。
  • 我的意思是,因为一旦你确定了你有哪些独特的元素,你必须把它们放在每个 中,你必须做 3 次测试,每个 td 一次,因为无法存储值并继续打印值。因此,即使您进行测试并发现 ZZZ 存在,并且您知道所有 td 的所有 3 个值,您必须在开始第二个 td 之前完成第一个 td。我暂时没有机会写下来,如果你还需要它会在几个小时内完成
  • 谢谢伍迪。如果你能把它写下来,我将不胜感激。不过,我会自己试一试,看看我是否能弄清楚你的意思。非常感谢。
猜你喜欢
相关资源
最近更新 更多
热门标签