2012-10-05 23 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属性)添加到一个面包屑链接中。 –