【问题标题】:XPath results from XSLT are incorrect - not going through entire XMLXSLT 的 XPath 结果不正确 - 没有通过整个 XML
【发布时间】:2010-08-10 16:46:15
【问题描述】:

所以我手上有一个非常令人困惑的问题..

在 XSL 中使用 XPath 表达式时,似乎没有搜索整个 XML 数据层次结构。

一些虚拟的 XML 数据:

<pets name="myPets" NUM="2">
    <dog name="allMyDogs" NUM="5">
        <dog name="Frank"  NUM="3"/>
        <dog name="Spot"  NUM="4"/>
        <dog name="Rover"  NUM="1"/>
        <dog name="Rupert" NUM="6"/>
        <cat name="Lucy"  NUM="4"/>
    </dog>
    <cat name="allMyCats" NUM="4">
        <cat name="Simba" NUM="4"/>
        <cat name="Princess"  NUM="5"/>
        <cat name="Fluffy" NUM="1"/>
        <cat name="Lucy"  NUM="3"/>
        <cat name="Lucy"  NUM="35"/>
        <cat name="Lucy"  NUM="6"/>
        <cat name="Lucy" NUM="1"/>
    </cat>
    <cat name="Lucy" NUM="9"/>
</pets>

以下是我认为导致问题的 XSLT 代码部分:

 <xsl:key name="elem_key" match="elem" use="concat(@key, .)" />

  <xsl:variable name="all_data">
    <xsl:apply-templates select="*">
      <xsl:sort select="name()" />
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:template match="//*[@NUM&lt;=4]">
    <elem key="{name()}">
      <xsl:copy-of select="@*" />
      <xsl:for-each select="@*">
        <xsl:sort select="name()" />
        <attribute>|<xsl:value-of select="name()" />|</attribute>
      </xsl:for-each>
    </elem>
  </xsl:template>

  <xsl:template match="/">
    <html>
      <body>
        <xsl:for-each select="msxsl:node-set($all_data)">
              <xsl:for-each select="*[generate-id()=generate-id(key('elem_key',concat(@key, .))[1])]">
                <table >
                  <tr>
                    <td>Element Name</td>
                    <xsl:for-each select="*">
                      <td>
                        <xsl:value-of select="translate(.,'|','')" />
                      </td>
                    </xsl:for-each>
                  </tr>
                  <xsl:for-each select="key('elem_key', concat(@key, .))">
                    <xsl:variable name="curr_elem" select="." />
                    <tr>
                      <td>
                        <xsl:value-of select="@key" />
                      </td>
                      <xsl:for-each select="*">
                        <td >
                          <xsl:value-of select="$curr_elem/@*[name()=translate(current(),'|','')]" />
                        </td>
                      </xsl:for-each>
                    </tr>
                  </xsl:for-each>
                </table>
                <p />
              </xsl:for-each>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

使用的 XPath 表达式:

//*[@NUM&lt;=4]

(上面应该会产生很多结果)

我得到的错误结果:

Element Name    name     NUM
pets            myPets   2

如您所见,它似乎停在了根部。

如果我将 XPath 更改为:

//*[@NUM=4]

我得到了这些不正确的结果:

Element Name  name    NUM
dog        Spot      4


Element Name  name      NUM
cat        Lucy      4


Element Name  name      NUM
cat        allMyCats   4

似乎发生的是,一旦找到匹配项,它将停止向下搜索层次结构。前两个(Spot 和 Lucy)是正确的,但随后在 allMyCats 处停止,此时 allMyCats (Simba) 的子节点的 NUM 为 4。

谁能帮我修复这段代码,让它返回正确的结果?我很沮丧! :(

谢谢!

【问题讨论】:

  • 没有完整的样式表,我只能猜测...如果 var all_data 被声明为样式表的顶部元素,那么我建议在 xsl:apply-templates 中使用绝对路径,例如“/*”
  • @Alejandro: 是样式表中的第一个元素,然后是 var all_data。我尝试了路径“/ *”,但它并没有影响我的结果。 :(
  • 好问题 (+1)。有关问题的解释和简单的解决方案,请参阅我的答案。 :)

标签: xml xslt xpath


【解决方案1】:

只需改变

  <xsl:variable name="all_data">
    <xsl:apply-templates select="*">
      <xsl:sort select="name()" />
    </xsl:apply-templates>
  </xsl:variable>

收件人

  <xsl:variable name="all_data">
    <xsl:apply-templates select="*/*">
      <xsl:sort select="name()" />
    </xsl:apply-templates>
  </xsl:variable>

第一个(许多)问题是

<xsl:apply-templates select="*">

选择当前节点的所有子元素。当前节点是/,它有只有一个子节点——顶部元素pets

您实际上想要收集pets 的所有子代的数据。

还有其他问题,但我没有空间和时间在这里解决。

完整的更正代码如下

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
version="1.0">

 <xsl:key name="elem_key" match="elem" 
      use="concat(@key, .)" />

  <xsl:variable name="all_data">
    <xsl:apply-templates select="*//*">
      <xsl:sort select="name()" />
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:template match="*[@NUM&lt;=4]">
    <elem key="{name()}">
      <xsl:copy-of select="@*" />
      <xsl:for-each select="@*">
        <xsl:sort select="name()" />
        <attribute>|<xsl:value-of 
             select="concat(name(),'=',.)" />|</attribute>
      </xsl:for-each>
    </elem>
  </xsl:template>

  <xsl:template match="/">
    <html>
      <body>
        <xsl:for-each select="msxsl:node-set($all_data)">
              <xsl:for-each select=
               "*[generate-id()
                 =
                  generate-id(key('elem_key',concat(@key, .))[1])
                 ]">
                <table >
                  <tr>
                    <td>Element Name</td>
                    <xsl:for-each select="*">
                      <td>
                        <xsl:value-of select=
                          "substring-before(translate(.,'|',''),'=')" />
                      </td>
                    </xsl:for-each>
                  </tr>
                    <tr>
                      <td>
                        <xsl:value-of select="@key" />
                      </td>
                      <xsl:for-each select="*">
                        <td>
                          <xsl:value-of select=
                          "substring-after
                              (translate(current(),'|',''),
                               '='
                               )"/>
                        </td>
                      </xsl:for-each>
                    </tr>
                </table>
                <p />
              </xsl:for-each>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时

<pets name="myPets" NUM="2">
    <dog name="allMyDogs" NUM="5">
        <dog name="Frank"  NUM="3"/>
        <dog name="Spot"  NUM="4"/>
        <dog name="Rover"  NUM="1"/>
        <dog name="Rupert" NUM="6"/>
        <cat name="Lucy"  NUM="4"/>
    </dog>
    <cat name="allMyCats" NUM="4">
        <cat name="Simba" NUM="4"/>
        <cat name="Princess"  NUM="5"/>
        <cat name="Fluffy" NUM="1"/>
        <cat name="Lucy"  NUM="3"/>
        <cat name="Lucy"  NUM="35"/>
        <cat name="Lucy"  NUM="6"/>
        <cat name="Lucy" NUM="1"/>
    </cat>
    <cat name="Lucy" NUM="9"/>
</pets>

产生了想要的结果(多只动物)

<html xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <body>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>Lucy</td>
                <td>4</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>allMyCats</td>
                <td>4</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>Simba</td>
                <td>4</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>Fluffy</td>
                <td>1</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>Lucy</td>
                <td>3</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>Lucy</td>
                <td>1</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>Frank</td>
                <td>3</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>Spot</td>
                <td>4</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>Rover</td>
                <td>1</td>
            </tr>
        </table>
        <p></p>
    </body>
</html>

【讨论】:

  • @Dimitre:我用 //*[@NUM = 4] 和 //*[@NUM <= 4] 尝试了这个,但我仍然没有在“allMyCats”下得到任何孩子跨度>
  • @iHeartGreek:那么您使用的是不同的代码或 XML 文件。我编辑了我的回复并将完整的转换、XML 文件和结果放在那里。您应该能够复制它们并从转换中获得相同的结果。
  • @Dimitre: For //*[@NUM<=4] 在您的代码中显示.. 我不会期望返回名为 Fluffy 和 Simba 的猫吗?哦,您的结果中也缺少 Lucy NUM=3 和 Lucy NUM=1。很抱歉,但我没有收到使用此代码的正确结果(精确复制粘贴):( 我在泡菜中。
  • @iHeartGreek:我已经解决了你抱怨的问题:只收到一个结果。现在你发现还有其他问题。只是问一个新问题。这个已经解决了。
  • @Alejandro:XSLT 1.0 w3.org/TR/xslt#top-level-variables 很好地指定了它:“在顶层,指定变量值的表达式或模板使用与用于处理根的上下文相同的上下文进行评估源文档的节点:当前节点是源文档的根节点,当前节点列表是一个仅包含源文档根节点的列表。"
【解决方案2】:

*[generate-id()=generate-id(key('elem_key',concat(@key, .))[1])]你有一个键做一个匹配几个案例的选择,然后你用 [1] 只要求第一个

我不清楚您想要什么样的布局作为进一步建议的正确结果。

【讨论】:

    猜你喜欢
    • 2014-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 2019-12-19
    • 1970-01-01
    • 1970-01-01
    • 2015-10-09
    相关资源
    最近更新 更多