XSLT嵌套模板

时间:2017-10-26 19:56:50

标签: xml xslt

您好我正在尝试为国家/省/市建立类似树的结构但由于某种原因我的模板呈现错误

XML

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="hierarchy.xsl" ?>
<data>

<place id="CA" name="Canada"/>

<place parent="CA" id="ON" name="Ontario"/>
<place parent="CA" id="QC" name="Quebec"/>
<place parent="CA" id="NS" name="Nova Scotia"/>
<place parent="CA" id="NB" name="New Brunswick"/>
<place parent="CA" id="MB" name="Manitoba"/>
<place parent="CA" id="BC" name="British Columbia"/>
<place parent="CA" id="PE" name="Prince Edward Island"/>
<place parent="CA" id="SK" name="Saskatchewan"/>
<place parent="CA" id="AB" name="Alberta"/>
<place parent="CA" id="NL" name="Newfoundland and Labrador"/>

<place parent="ON" name="Barrie"/>
<place parent="ON" name="Belleville"/>
<place parent="ON" name="Brampton"/>
<place parent="ON" name="Brant"/>
<place parent="ON" name="Brantford"/>
<place parent="ON" name="Brockville"/>
<place parent="QC" name="Acton Vale"/>
<place parent="QC" name="Alma"/>
<place parent="QC" name="Amos"/>
<place parent="QC" name="Amqui"/>
<place parent="QC" name="Asbestos"/>
<place parent="MB" name="Brandon"/>
<place parent="MB" name="Dauphin"/>
<place parent="MB" name="Flin Flon"/>
<place parent="MB" name="Morden"/>
<place parent="MB" name="Portage la Prairie"/>
<place parent="PE" name="Charlottetown"/>
<place parent="PE" name="Summerside"/>
<place parent="PE" name="Alberton"/>
<place parent="PE" name="Borden-Carleton"/>
<place parent="PE" name="Cornwall"/>
<place parent="PE" name="Georgetown"/>
<place parent="PE" name="Kensington"/>
<place parent="PE" name="Montague"/>

<place parent="FR" id="LL" name="Newfoundland and Labrador"/>
<place id="FR" name="France"/>
<place id="JP" name="Japan"/>

</data>

我的XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
  <body>
    <ul>
      <xsl:apply-templates select="//place"/>
    </ul>
  </body>
</html>
</xsl:template>
<xsl:template match="place[not(@parent)]">
<li> <xsl:value-of select="@name" /></li>
<xsl:apply-templates select="place[@parent='CA']"/>
</xsl:template>
<xsl:template match="place[@parent='CA']">
<ul>
  <li> <xsl:value-of select="@name" /></li>
  <xsl:apply-templates select="place[@parent='ON']"/>
</ul>
</xsl:template>
<xsl:template match="place[@parent='ON']">
<li> <xsl:value-of select="@name" /></li>
</xsl:template>
</xsl:stylesheet>

我得到的输出是

加拿大   各省 城市

现在的问题是,在所有省份都被渲染后,安大略省的城市正在渲染。我需要帮助。

2 个答案:

答案 0 :(得分:0)

有点难以准确地说出你想要的是什么,因为你没有表现出你想要的结果,但我还是会尝试。

建议xsl:key使用place属性引用parent

然后,您可以使用当前的id属性来应用模板。

示例...

XML输入

<data>

    <place id="CA" name="Canada"/>

    <place parent="CA" id="ON" name="Ontario"/>
    <place parent="CA" id="QC" name="Quebec"/>
    <place parent="CA" id="NS" name="Nova Scotia"/>
    <place parent="CA" id="NB" name="New Brunswick"/>
    <place parent="CA" id="MB" name="Manitoba"/>
    <place parent="CA" id="BC" name="British Columbia"/>
    <place parent="CA" id="PE" name="Prince Edward Island"/>
    <place parent="CA" id="SK" name="Saskatchewan"/>
    <place parent="CA" id="AB" name="Alberta"/>
    <place parent="CA" id="NL" name="Newfoundland and Labrador"/>

    <place parent="ON" name="Barrie"/>
    <place parent="ON" name="Belleville"/>
    <place parent="ON" name="Brampton"/>
    <place parent="ON" name="Brant"/>
    <place parent="ON" name="Brantford"/>
    <place parent="ON" name="Brockville"/>
    <place parent="QC" name="Acton Vale"/>
    <place parent="QC" name="Alma"/>
    <place parent="QC" name="Amos"/>
    <place parent="QC" name="Amqui"/>
    <place parent="QC" name="Asbestos"/>
    <place parent="MB" name="Brandon"/>
    <place parent="MB" name="Dauphin"/>
    <place parent="MB" name="Flin Flon"/>
    <place parent="MB" name="Morden"/>
    <place parent="MB" name="Portage la Prairie"/>
    <place parent="PE" name="Charlottetown"/>
    <place parent="PE" name="Summerside"/>
    <place parent="PE" name="Alberton"/>
    <place parent="PE" name="Borden-Carleton"/>
    <place parent="PE" name="Cornwall"/>
    <place parent="PE" name="Georgetown"/>
    <place parent="PE" name="Kensington"/>
    <place parent="PE" name="Montague"/>

    <place parent="FR" id="LL" name="Newfoundland and Labrador"/>
    <place id="FR" name="France"/>
    <place id="JP" name="Japan"/>

</data>

XSLT 1.0

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

  <!--This key matches "place" elements that have a "parent" attribute.
  The "parent" attribute is used as the value of the key.
  For example, <place parent="CA" id="ON" name="Ontario"/> would
  have the key "CA" (along with "Quebec", "Nova Scotia", etc.).-->
  <xsl:key name="places" match="place[@parent]" use="@parent"/>

  <!--This template matches the root element "data".-->
  <xsl:template match="/data">
    <html>
      <body>
        <ul>
          <!--We apply-templates only to "place" elements that do not
          have a "parent" attribute (ie "Canada", "France", and 
          "Japan"). These elements will be processed in document
          order. Since "Canada" appears first in the document, it
          will be processed first.-->
          <xsl:apply-templates select="place[not(@parent)]"/>
        </ul>
      </body>
    </html>
  </xsl:template>

  <!--This template matches any "place" element.-->
  <xsl:template match="place">
    <li>
      <xsl:value-of select="@name"/>
      <!--This xsl:if is used to check to see if there 
      are any "place" elements with the key of the "id" attribute.
      For example, if the current "place" element was 
      <place id="CA" name="Canada"/> the test would resolve to 
      "key('places','CA')". If there are "place" elements with
      that key, we output a new "ul" and apply-templates to those
      elements.-->
      <xsl:if test="key('places',@id)">
        <ul>
          <!--Here is where we apply-templates (process) to elements 
          using the "id" attribute of the current "place" element.
          For example, if the current "id" attribute was "CA", we'd
          be processing any "place" elements with the "parent" attribute
          value "CA" (which is what the key was created with ("use" 
          attribute of "xsl:key" above)). This all happens in document
          order.-->
          <xsl:apply-templates select="key('places',@id)"/>        
        </ul>        
      </xsl:if>
    </li>
  </xsl:template>

</xsl:stylesheet>

HTML输出

&#13;
&#13;
<html>
   <body>
      <ul>
         <li>Canada
            <ul>
               <li>Ontario
                  <ul>
                     <li>Barrie</li>
                     <li>Belleville</li>
                     <li>Brampton</li>
                     <li>Brant</li>
                     <li>Brantford</li>
                     <li>Brockville</li>
                  </ul>
               </li>
               <li>Quebec
                  <ul>
                     <li>Acton Vale</li>
                     <li>Alma</li>
                     <li>Amos</li>
                     <li>Amqui</li>
                     <li>Asbestos</li>
                  </ul>
               </li>
               <li>Nova Scotia</li>
               <li>New Brunswick</li>
               <li>Manitoba
                  <ul>
                     <li>Brandon</li>
                     <li>Dauphin</li>
                     <li>Flin Flon</li>
                     <li>Morden</li>
                     <li>Portage la Prairie</li>
                  </ul>
               </li>
               <li>British Columbia</li>
               <li>Prince Edward Island
                  <ul>
                     <li>Charlottetown</li>
                     <li>Summerside</li>
                     <li>Alberton</li>
                     <li>Borden-Carleton</li>
                     <li>Cornwall</li>
                     <li>Georgetown</li>
                     <li>Kensington</li>
                     <li>Montague</li>
                  </ul>
               </li>
               <li>Saskatchewan</li>
               <li>Alberta</li>
               <li>Newfoundland and Labrador</li>
            </ul>
         </li>
         <li>France
            <ul>
               <li>Newfoundland and Labrador</li>
            </ul>
         </li>
         <li>Japan</li>
      </ul>
   </body>
</html>
&#13;
&#13;
&#13;

这是样式表如何处理较小数据集的简化版本(忽略 结果树片段,序列化等):

<data>
    <place id="CA" name="Canada"/>
    <place parent="CA" id="ON" name="Ontario"/>
    <place parent="ON" name="Barrie"/>
    <place parent="FR" id="LL" name="Newfoundland and Labrador"/>
    <place id="FR" name="France"/>
</data>
  1. <data>与第一个模板(match="/data")匹配。
  2. &#34; html&#34;,&#34; body&#34;,and first&#34; ul&#34;元素输出。
  3. 在&#34; ul&#34;我们仅处理(选择)没有<place>属性的parent元素(加拿大和法国)。
  4. <place id="CA" name="Canada"/>与第二个模板(match="place")匹配。
  5. &#34; li&#34;元素输出时带有name属性值(&#34;加拿大&#34;)。
  6. 在&#34; li&#34;里面,我们检查一下键是否放置&#34;包含任何带有键的节点&#34; CA&#34; (基于当前id元素的place属性值)。
  7. 有一个带有密钥的节点&#34; CA&#34; (<place parent="CA" id="ON" name="Ontario"/>)所以它会输出一个新的&#34; ul&#34;元件。
  8. 在UL内部,我们使用该密钥处理节点。
  9. <place parent="CA" id="ON" name="Ontario"/>与第二个模板(match="place")匹配。
  10. &#34; li&#34; element使用name属性值输出(&#34; Ontario&#34;)。
  11. 在&#34; li&#34;里面,我们检查一下键是否放置&#34;包含任何带有键的节点&#34; ON&#34; (基于当前id元素的place属性值)。
  12. 有一个带有键的节点&#34; ON&#34; (<place parent="ON" name="Barrie"/>)所以它会输出一个新的&#34; ul&#34;元件。
  13. 在UL内部,我们使用该密钥处理节点。
  14. <place parent="ON" name="Barrie"/>与第二个模板(match="place")匹配。
  15. &#34; li&#34;元素输出时带有name属性值(&#34; Barrie&#34;)。
  16. 在&#34; li&#34;内部,密钥测试失败,因为当前place元素没有id元素。
  17. <place id="CA" name="Canada"/>的处理已完成。
  18. 处理没有place属性的下一个parent元素。 (基本上它会回到第3步继续处理。)
  19. <place id="FR" name="France"/>与第二个模板(match="place")匹配。
  20. &#34; li&#34;元素以name属性值输出(&#34; France&#34;)。
  21. 在&#34; li&#34;里面,我们检查一下键是否放置&#34;包含任何带有键的节点&#34; FR&#34; (基于当前id元素的place属性值)。
  22. 有一个带有键的节点&#34; FR&#34; (<place parent="FR" id="LL" name="Newfoundland and Labrador"/>)所以它会输出一个新的&#34; ul&#34;元件。
  23. 在UL内部,我们使用该密钥处理节点。
  24. <place parent="FR" id="LL" name="Newfoundland and Labrador"/>与第二个模板(match="place")匹配。
  25. &#34; li&#34;元素以name属性值输出(&#34;纽芬兰和拉布拉多&#34;)。
  26. 在&#34; li&#34;里面,我们检查一下键是否放置&#34;包含任何带有键的节点&#34; LL&#34; (基于当前id元素的place属性值)。
  27. 没有带键的节点&#34; LL&#34;所以<place parent="FR" id="LL" name="Newfoundland and Labrador"/>的处理完成了。
  28. 不再有place个元素没有parent属性可供处理,因此所有处理都已完成。
  29. 希望我没有错过任何步骤。

    另请参阅规范部分"5. Template Rules"以获取更多信息(尤其是5.1和5.8)。

答案 1 :(得分:0)

让我们先来看看你的代码错误的原因。

<xsl:template match="/">
...
      <xsl:apply-templates select="//place"/>
...
</html>

在这里,您将处理所有地点,无论其在逻辑层次结构中的深度如何。

<xsl:template match="place[not(@parent)]">
  <li> <xsl:value-of select="@name" /></li>
  <xsl:apply-templates select="place[@parent='CA']"/>
</xsl:template>

在这里看起来好像您正在尝试通过输出地点的名称然后处理其子项来处理顶级地点。但是输入中没有任何地方有任何(XML)子元素,它们都是空元素。所以xsl:apply-templates在这里什么都不做。如果它确实做了某些事情,它就会做错事,因为你会在两次处理非顶级位置,一次从根模板处理,一次从父模板处理。

我认为你想要做的是逻辑(地理)层次结构的递归下降,这与XML树层次结构不同。这样做的方法是,无论何时处理层次结构中的节点,它都应输出自己的详细信息,然后应用模板来处理其逻辑子节点:

<xsl:template match="place">
  <li>
    <p><xsl:value-of select="@name"/></p>
    <ul>
      <xsl:apply-templates select="(logical children)"/>
    </ul>
  </li>
</xsl:template>

选择&#34;逻辑子女的一种方法&#34;是select="//place[@parent=current()/@id]"

但除非您使用XSLT处理器(例如Saxon-EE)并使用非常好的优化器,否则您将通过定义密钥获得更好的性能:

<xsl:key name="k" match="place" use="@id"/>

然后

select="key('k', @parent)"