2012-10-05 69 views
2

我有困難寫生成麪包屑試製出的節點結構的模板。它不能正常工作到現在爲止,沒有在我的思想應該怎麼走項目路徑的一些缺陷。生成與XSL一個麪包屑從節點結構

考慮下面的頁面結構:

<!-- ===== SITE PAGE STRUCTURE ===================================== --> 
<index> 
    <item section="home" id="index"></item> 
    <item section="service" id="index"> 
     <item id="content-management-systems"> 
     <item id="p1-1"/> 
     <item id="p1-2"/> 
     <item id="p1-3"/> 
     </item> 
     <item id="online-stores"></item> 
     <item id="search-engines-and-ir"></item> 
     <item id="web-applications"></item> 
    </item> 

    <item section="solutions" id="index"> 
     <item id="document-clustering"></item> 
    </item> 
    <item section="company" id="index"> 
     <item section="company" id="about"></item> 
     <item section="company" id="philosophy" ></item> 
     ... 
    </item> 
... 
</item> 

本網站指數代表了其層次結構的XML內容頁(認爲它是一個菜單)網站結構。它包含部分,代表的網站部分就像家庭,公司,服務,解決方案等。這些部分可以包含分部分與頁面,或只是普通的內容頁面。內容頁面(其標題,文本內容等xml內容)由項目樹中的@id屬性標識。 @id屬性主要用於獲取將呈現爲html的整個頁面的內容。 痕跡導航模板使用項目節點@Id屬性讓頁面(這將在導航條來顯示)的稱號。

我嘗試實現以下模板,步行通過檢查樹中的目標部分屬性@section和目標頁面屬性@id。我希望它走線,直到目標item_target通過比較祖先@section屬性,並與該軸的每個節點的item_target $的@id找到。

例如:屬性* $ item_section =服務*和頁面ID *目標,item_target = P1-1 *現在應該遞歸 「走」 到部分分支 「服務」(深度爲1),檢查是否目標頁面@id在此級別上找到。在這種情況下,它沒有找到,所以它使下一recurive調用(通過應用模板)到下一個項目節點級(在這種情況下,這將是內容管理系統,有目標項目頁面P1 -1被發現,所以跟蹤過程完成:

結果應該是這樣的:

首頁>>服務>>內容管理系統>> P1-1

但不幸的是它是沒有正確的工作,至少不是在所有情況下,也可能更容易解決把它作爲一個遞歸模板,從頂層(0級)到目標頁面(項目節點)作爲一個葉子。

<!-- walk item path to generate a breadcrumb trail --> 
    <xsl:template name="breadcrumb"> 
     <a> 
      <xsl:attribute name="href"> 
       <xsl:text>/</xsl:text> 
       <xsl:value-of select="$req-lg"/> 
       <xsl:text>/home/index</xsl:text> 
      </xsl:attribute> 
      <xsl:value-of select="'Home'"/> 
     </a> 

     <xsl:apply-templates select="$content/site/index" mode="Item-Path"> 
      <xsl:with-param name="item_section" select="'service'"/> 
      <xsl:with-param name="item_target" select="'search-engines-and-ir'"/> 
      <xsl:with-param name="depth" select="0"/> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="item" mode="Item-Path"> 
     <xsl:param name="item_section" /> 
     <xsl:param name="item_target" /> 
     <xsl:param name="depth" /> 
     <!-- 
     depth=<xsl:value-of select="$depth"/> 
     count=<xsl:value-of select="count(./node())"/><br/> 
--> 
     <xsl:variable name="cur-id" select="@id"/> 
     <xsl:variable name="cur-section" select="@section"/> 
     <xsl:choose>  
      <xsl:when test="@id=$item_target"> 
       &gt;&gt; 
       <a> 
        <xsl:attribute name="href"> 
         <xsl:text>/</xsl:text> 
              <!-- req-lg: global langauge variable --> 
         <xsl:value-of select="$req-lg"/> 
         <xsl:text>/</xsl:text> 
         <xsl:value-of select="$item_section"/> 
         <xsl:text>/</xsl:text> 
         <xsl:if test="$depth = 2"> 
          <xsl:value-of select="../@id"/> 
          <xsl:text>/</xsl:text> 
         </xsl:if> 
         <xsl:value-of select="@id"/> 
        </xsl:attribute> 
        <xsl:value-of 
         select="$content/page[@id=$cur-id]/title"/> 
       </a> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:if test="ancestor-or-self::item/@section = $item_section and count(./node()) > 0"> 
       &gt;&gt;: 
       <a> 
        <xsl:attribute name="href"> 
         <xsl:text>/</xsl:text> 
              <!-- req-lg: global langauge variable --> 
         <xsl:value-of select="$req-lg"/> 
         <xsl:text>/</xsl:text> 
         <xsl:value-of select="$item_section"/> 
         <xsl:text>/</xsl:text> 
         <xsl:if test="$depth = 2"> 
          <xsl:value-of select="../@id"/> 
          <xsl:text>/</xsl:text> 
         </xsl:if> 
         <xsl:value-of select="@id"/> 
        </xsl:attribute> 
        <xsl:value-of 
         select="$content/page[@id=$cur-id and @section=$item_section]/title"/> 
       </a> 
       </xsl:if> 
      </xsl:otherwise> 
     </xsl:choose> 

     <xsl:apply-templates select="item" mode="Item-Path"> 
      <xsl:with-param name="item_section" select="$item_section"/> 
      <xsl:with-param name="item_target" select="$item_target"/> 
      <xsl:with-param name="depth" select="$depth + 1"/> 
     </xsl:apply-templates> 

    </xsl:template> 

,以便在模板中的硬編碼參數麪包屑,目標節=「服務」和目標網頁=「搜索引擎和-IR」,我希望像

回家輸出>>服務>>搜索引擎和紅外

但輸出

首頁>>服務>>內容管理系統>> S目錄操作搜索,引擎和紅外

這顯然是不正確的。

任何人都可以給我一個提示如何解決這個問題?避免深度檢查會更加優雅,但到現在爲止,我無法想到其他方式,我確信有更優雅的解決方案。

我使用XSLT 1.0(通過PHP5的libxml)。

希望我的問題很清楚,如果不是,請提問:-)感謝您的幫助!

+0

的問題是不明確的。請編輯並解釋在源XML文檔中如何表示父子關係。另外,請解釋給出了什麼以及必須在樹*的條款中產生什麼。 –

+0

@ Dimitre Novatchev:對不起,如果我的問題不清楚。我編輯了我的問題,並試圖更準確地看到xml樹和xsl模板代碼之間的文本。希望它更清楚我試圖實現的目標...... –

+0

在您的輸入文檔中,服務不是家中的孩子。你的預期產出不應該簡單地......服務>>搜索引擎和搜索引擎? –

回答

2

就是這麼簡單此

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="text"/> 

<xsl:key name="kNodeById" match="item" use="@id"/> 

<xsl:template match="/"> 
    <xsl:text>home</xsl:text> 
    <xsl:call-template name="findPath"> 
    <xsl:with-param name="pStart" select="'service'"/> 
    <xsl:with-param name="pEnd" select="'search-engines-and-ir'"/> 
    </xsl:call-template> 
</xsl:template> 

<xsl:template name="findPath"> 
    <xsl:param name="pStart"/> 
    <xsl:param name="pEnd"/> 

    <xsl:for-each select= 
    "key('kNodeById', $pEnd) 
     [ancestor::item[@section=$pStart]] 
     [1] 
     /ancestor-or-self::item 
       [not(descendant::item[@section=$pStart])] 
    "> 

    <xsl:value-of select= 
    "concat('>>', @id[not(../@section)], @section)"/> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

的希望,正確的結果產生:

home>>service>>search-engines-and-ir 

請注意

  1. 該解決方案打印從任何節點的麪包屑 - 層次結構中的任何地方,以任何派生節點 - - 層次結構中的任何位置。更確切地說,對於第一個item(按文檔順序),id屬性等於$pEnd,麪包屑從其最內部的祖先(其section屬性等於$pStart)生成到該item元素。

  2. 該解決方案應該比使用//的任何解決方案效率高得多,因爲我們使用密鑰來有效定位「結束」元素item


II。 XSLT 2.0溶液:

更短且容易 - 一個XPathe 2.0單個表達式:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="text"/> 

<xsl:key name="kNodeById" match="item" use="@id"/> 

<xsl:template match="/"> 
    <xsl:value-of select= 
    "string-join(
     (
     'home', 
     key('kNodeById', $pEnd) 
      [ancestor::item[@section=$pStart]] 
       [1] 
       /ancestor-or-self::item 
       [not(descendant::item[@section=$pStart])] 
         /(@id[not(../@section)], @section)[1] 

     ), 
     '>>' 
     ) 
    "/> 
</xsl:template> 
</xsl:stylesheet> 
+0

好的答案!感謝那。其實我從來沒有用過'key()'函數。我稍微修改了xslt 1.0解決方案,在for-each循環中將* ancestor :: item *更改爲ancestor-or-self :: item,以便能夠獲取樹的第一級項目(例如*服務*,*解決方案*等)。因爲麪包圈完全可以工作。此外,我使用該模板(技術)來生成每個麪包屑的URL(路徑部分)! –

+0

@ AndreasW.Wylach,不客氣。密鑰是XSLT的一個非常強大的功能,值得一提,以瞭解和使用此功能。如果僅限於XSLT 1.0,最有效的分組方法是使用密鑰(Muenchian分組方法)。 –

1

您可以迭代祖先或自我::軸。這很容易做到沒有遞歸。例如...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="html"/> 

<xsl:template match="/"> 
    <html><body> 
    <xsl:call-template name="bread-crumbs"> 
     <xsl:with-param name="items" select="*/item" /> 
     <xsl:with-param name="section" select="'service'" /> 
     <xsl:with-param name="leaf" select="'p1-2'" /> 
    </xsl:call-template> 
    </body></html> 
</xsl:template> 

<xsl:template name="bread-crumbs"> 
    <xsl:param name="items" /> 
    <xsl:param name="section" /> 
    <xsl:param name="leaf" /> 
    <xsl:value-of select="concat($section,'&gt;&gt;')" /> 
    <xsl:for-each select="$items/self::item[@section=$section]//item[@id=$leaf]/ 
         ancestor-or-self::item[not(@section)]"> 
    <xsl:value-of select="@id" /> 
    <xsl:if test="position() != last()"> 
     <xsl:value-of select="'&gt;&gt;'" /> 
    </xsl:if> 
    </xsl:for-each> 
</xsl:template> 

</xsl:stylesheet> 

...您的樣品輸入產量...

<html> 
    <body>service&gt;&gt;content-management-systems&gt;&gt;p1-2</body> 
</html> 

...這使得作爲...

service>>content-management-systems>>p1-2 
+0

感謝您的回答!我試了一下,它正在工作。 Dimitre提供了一個解決方案,我也可以輕鬆地將樹的url(href屬性)添加到一個麪包屑鏈接中。 –