在xml上使用xsl:for-each来决定显示哪些标签值对?

时间:2013-09-24 05:10:12

标签: xml xslt

我开始使用XSLT我想知道我是否可以这样做:

我们正在使用此示例http://www.w3schools.com/xsl/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog并对其进行一些修改。

将以下内容粘贴到XSLT区域。

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

<xsl:template match="/">


  <html>
  <body>
  <h2>My CD Collection</h2>
    <table border="1">
      <tr bgcolor="#9acd32">
        <th>Title</th>
        <th>Artist</th>
      </tr>
      <xsl:for-each select="catalog/cd">
      <xsl:choose>
<xsl:when test="country = 'USA'"> 
    <tr>
        <td>Title:</td> <td><xsl:value-of select="title"/></td>
        <td>Artist</td> <td><xsl:value-of select="artist"/></td>
        <td>Year:</td>  <td><xsl:value-of select="year"/></td>
    </tr>
</xsl:when>

<xsl:otherwise>
    <tr>
        <td>Price:</td>     <td><xsl:value-of select="price"/></td>
        <td>Company:</td>   <td><xsl:value-of select="company"/></td>
    </tr>
</xsl:otherwise>
</xsl:choose>

      </xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

我们在这里得到的是一个常见的XML源,但是根据XML元素的一个节点(在本例中是国家/地区),我们希望以不同的方式显示数据。

到目前为止一切顺利。

现在我们要做的是使用XML结构来指定要根据国家/地区显示的每个Label to Node对。然后使用for-each循环迭代所有对并显示它们。

理性的是,格式可能比简单的LabelValue稍微复杂一点,我们不必在以后手动更改所有这些格式。

这就是我尝试过的。

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

<xsl:template match="/">
<xsl:variable name="Details">
    <Details>
        <USA>
            <Pair><Node>title</Node><Label>Title:</Label></Pair>
            <Pair><Node>artist</Node><Label>Artist:</Label></Pair>
            <Pair><Node>year</Node><Label>Year:</Label></Pair>
    </USA>
    <Others>
            <Pair><Node>price</Node><Label>Price:</Label></Pair>
            <Pair><Node>company</Node><Label>Company:</Label></Pair>
    </Others>
</Details>
</xsl:variable>


  <html>
  <body>
  <h2>My CD Collection</h2>
    <table border="1">
      <tr bgcolor="#9acd32">
        <th>Title</th>
        <th>Artist</th>
      </tr>
      <xsl:for-each select="catalog/cd">
      <xsl:choose>
<xsl:when test="country = 'USA'"> 

    <xsl:for-each select = "$Details/Details/USA/Pair">
        <xsl:variable name="Node">
            <xsl:value-of select ="Node"/>
        </xsl:variable> 
        <tr><td><xsl:value-of select = "Label"/></td><td> <xsl:value-of select ="Node"/></td></tr>
    </xsl:for-each>

</xsl:when>
<xsl:otherwise>
    <xsl:for-each select = "$Details/Details/Others/Pair">
        <xsl:variable name="Node">
            <xsl:value-of select ="Node"/>
        </xsl:variable> 
        <tr><td><xsl:value-of select = "Label"/></td><td> <xsl:value-of select ="Node"/></td></tr>
    </xsl:for-each>
</xsl:otherwise>
</xsl:choose>

      </xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

但这不起作用。

有人能指出我正确的方向吗?

1 个答案:

答案 0 :(得分:4)

问题在于XSLT 1.0, $ Details 变量包含“结果树片段”,而不是“节点集”,而XSLT需要它作为节点集来访问它以你需要的方式。要在XSLT 1.0中解决此问题,您需要使用扩展函数将结果树片段转换为节点集。您使用哪种扩展功能取决于您当前的平台。对于此示例,我使用的是Micorsoft,但Microsoft以外最常见的是EXSLT。 (见http://www.xml.com/pub/a/2003/07/16/nodeset.html

通过定义扩展功能,您可以执行此操作

<xsl:for-each select="msxsl:node-set($Details)/Details/USA/Pair">

你仍然需要做一些调整来实现你的目标。在此循环中,您现在将位于 Pair 元素上,但您需要在外部循环中引用回原始的 cd 。要做到这一点,你需要先定义一个变量来保存对 cd 的引用(这需要在内部循环之前)。

 <xsl:variable name="cd" select="." />

然后,在内部循环中,您可以执行此操作以获取相关字段

<xsl:value-of select ="$cd/*[local-name() = current()/Node]"/>

值得一提的是,使用新代码,您有机会减少代码重复。您可以放弃 xsl:choose ,只需要一个内部 xsl:for-each

<xsl:for-each 
     select="$Details/*[local-name()=current()/country or local-name()='Others'][1]/Pair">

如果匹配的国家/地区存在,则选择匹配的国家/地区,否则将使用“其他”

这是完整的XSLT

<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="Details">
    <Details>
        <USA>
            <Pair><Node>title</Node><Label>Title:</Label></Pair>
            <Pair><Node>artist</Node><Label>Artist:</Label></Pair>
            <Pair><Node>year</Node><Label>Year:</Label></Pair>
    </USA>
    <Others>
            <Pair><Node>price</Node><Label>Price:</Label></Pair>
            <Pair><Node>company</Node><Label>Company:</Label></Pair>
    </Others>
</Details>
</xsl:variable>

<html>
    <body>
    <h2>My CD Collection</h2>
      <table border="1">
        <tr bgcolor="#9acd32">
          <th>Title</th>
          <th>Artist</th>
        </tr>
        <xsl:for-each select="catalog/cd">
          <xsl:variable name="cd" select="." />
          <xsl:for-each select="msxsl:node-set($Details)/Details/*[local-name()=current()/country or local-name()='Others'][1]/Pair">
            <tr>
              <td>
                <xsl:value-of select = "Label"/>
              </td>
              <td>
                <xsl:value-of select ="$cd/*[local-name() = current()/Node]"/>
              </td>
            </tr>
          </xsl:for-each>
        </xsl:for-each>
      </table>
    </body>
  </html>
</xsl:template>
</xsl:stylesheet>

在XSLT 2.0中,情况有所不同, $ Details 包含'序列',您不需要使用扩展功能。

还有另一种方法可以实现这一点,没有扩展功能,那就是使用 document()函数让XSTL引用自身。您只需包含一个XML片段

,而不仅仅是一个变量
  <my:Details>
    <Details>
     ...
    </Details>
  </my:Details>

然后你可以声明一个变量来引用它。因为您直接引用输入文档,所以将其视为节点集。

 <xsl:variable name="Details" select="document('')/*/my:Details" />

尝试这个XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my" exclude-result-prefixes="my">
  <my:Details>
    <Details>
    <USA>
      <Pair>
        <Node>title</Node>
        <Label>Title:</Label>
      </Pair>
      <Pair>
        <Node>artist</Node>
        <Label>Artist:</Label>
      </Pair>
      <Pair>
        <Node>year</Node>
        <Label>Year:</Label>
      </Pair>
    </USA>
    <Others>
      <Pair>
        <Node>price</Node>
        <Label>Price:</Label>
      </Pair>
      <Pair>
        <Node>company</Node>
        <Label>Company:</Label>
      </Pair>
    </Others>
    </Details>
  </my:Details>

  <xsl:template match="/">
    <xsl:variable name="Details" select="document('')/*/my:Details" />
    <html>
      <body>
        <h2>My CD Collection</h2>
        <table border="1">
          <tr bgcolor="#9acd32">
            <th>Title</th>
            <th>Artist</th>
          </tr>
          <xsl:for-each select="catalog/cd">
            <xsl:variable name="cd" select="." />
            <xsl:for-each select = "$Details/Details/*[local-name()=current()/country or local-name()='Others'][1]/Pair">
              <tr>
                <td>
                  <xsl:value-of select = "Label"/>
                </td>
                <td>
                  <xsl:value-of select ="$cd/*[local-name() = current()/Node]"/>
                </td>
              </tr>
            </xsl:for-each>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>