2

我很难编写一个从节点结构生成面包屑试验的模板。到目前为止它无法正常工作,我认为它应该如何走项目路径存在一些缺陷。

考虑以下页面结构:

<!-- ===== 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 来实现以下遍历树的模板。我希望它通过将祖先的@section 属性和@id 与该轴中每个节点的$item_target 进行比较来找到目标item_target,直到找到目标为止。

例如:属性 *$item_section=service* 和页面 id *target item_target=p1-1* 现在应该递归地“步行”到部分​​分支“ service ”(深度 1),检查目标页面 @id 是否在这个级别。在这种情况下,它没有找到,因此它对下一个项目节点级别进行下一次递归调用(通过应用模板)(在这种情况下它将是content-management-systems,在那里找到目标项目页面 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>

因此,作为模板面包屑中的硬编码参数,目标部分 = 'service' 和目标页面 = 'search-engines-and-ir',我希望输出类似

首页 >> 服务 >> search-engines-and-ir

但输出是

主页 >> 服务 >> 内容管理系统 >> search-engines-and-ir

这显然是不正确的。

谁能给我一个提示如何纠正这个问题?避免这种深度检查会更加优雅,但到目前为止我想不出其他方法,我相信有一个更优雅的解决方案。

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

希望我的问题足够清楚,如果没有,请询​​问:-)提前感谢您的帮助!

4

2 回答 2

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$pEndsection$pStartitem

  2. 这个解决方案应该比任何使用 的解决方案更有效//,因为我们正在使用一个键来有效地定位“结束”item元素。


二、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>
于 2012-10-05T05:19:26.717 回答
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
于 2012-10-05T04:51:52.973 回答