基于节点值的递归 XSLT 1.0

Recursive XSLT 1.0 based on node value

我试图在 XSLT 1.0 中实现递归(树)列表,从 XML 开始,看起来像这样:

<list>
  <row>
    <icon>http://server/app/icon.gif</icon>
    <title>Document</title>
    <location>Root\Formulier</location> 
  </row>
  <row>
    <icon>http://server/app/icon.gif</icon>
    <title>Handleiding1</title>
    <location>Root\Handleidingen</location> 
  </row>
  <row>
    <icon>http://server/app/icon.gif</icon>
    <title>Form</title>
    <location>Root\Formulier\Informed consent (IC)</location> 
  </row>
  <row>
    <icon>http://server/app/icon.gif</icon>
    <title>Handleiding2</title>
    <location>Root\Handleidingen</location> 
  </row>
</list>

这得用XSLT 1.0,因为我们的SharePoint还不支持2.0

它在 Windows Explorer 中看起来应该像一棵树。

我当前的 XSLT 代码是:

    <?xml version="1.0" encoding="utf-8"?>
<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:output method="xml" indent="yes" />
  <xsl:key name="groups" match="/list/row" use="location" />
  <xsl:template match="/list">
    <div class="idocument-list">
      <xsl:apply-templates select="row[generate-id() = generate-id(key('groups', location)[1])]"/>
    </div>
  </xsl:template>
  <xsl:template match="row">
    <div style="margin-bottom:5px;">
      <ul>
        <li>
          <img border="0" style="align:left;" src="/_layouts/15/images/folder.gif?rev=23" alt="map" />
          <span class="ms-textLarge idocumentlist-title">
            <xsl:value-of select="substring-after(location,'Root\')"/>
          </span>
          <ul style="display:none;">
            <xsl:for-each select="key('groups', location)">
              <li>
                <img border="0" style="align:left;">
                  <xsl:attribute name="src">
                    <xsl:value-of select="icon"/>
                  </xsl:attribute>
                </img>
                <span>
                  <a>
                    <xsl:attribute name="href">
                      <xsl:value-of select="link"/>
                    </xsl:attribute>
                    <xsl:value-of select="title"/>
                  </a>
                </span>
              </li>
            </xsl:for-each>
          </ul>
        </li>
      </ul>
    </div>
  </xsl:template>
</xsl:stylesheet>

显示的结果如下:

例如 'Formulier\Informed consent (IC)' 显示所有文件夹,而它应该在 \ 上拆分并显示 'Formulier' 作为 'Informed consent (IC)' 的父文件夹。 (我将 'Root\' 位置进行了子字符串化,但它应该作为根节点显示在顶部)

示例结果代码:

<div class="idocument-list">
  <ul>
    <li>
      <img style="align: left;" alt="map" src="..." border="0">
      <span class="ms-textLarge idocumentlist-title">Root</span>
      <ul>
        <li>
          <img style="align: left;" alt="map" src="..." border="0">
          <span class="ms-textLarge idocumentlist-title">Formulier</span>
          <ul>
            <li>
              <img style="align: left;" alt="map" src="..." border="0">
              <span class="ms-textLarge idocumentlist-title">Informed consent (IC)</span>
              <ul>
                <li>
                  <img style="align: left;" src="..." border="0">
                  <span>
                    <a href="...">Form</a>
                  </span>
                </li>
              </ul>
            </li>
            <li>
              <img style="align: left;" alt="map" src="..." border="0">
              <span>
                <a href="...">Document</a>
              </span>
            </li>
          </ul>
        </li>
        <li>
          <img style="align: left;" alt="map" src="..." border="0">
          <span class="ms-textLarge idocumentlist-title">Handleidingen</span>
          <ul>
            <li>
              <img style="align: left;" src="..." border="0">
              <span>
                <a href="...">Handleiding1</a>
              </span>
            </li>
            <li>
              <img style="align: left;" src="..." border="0">
              <span>
                <a href="...">Handleiding2</a>
              </span>
            </li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</div>

任何人都可以给我一个信息源或代码来使用 XSLT 1.0 来实现这样的事情吗?

提前致谢。

尼尔斯

恐怕这可能比看起来要复杂得多。

  • 如果要为每个文件夹显示一个节点(是否包含 文档或不),您必须首先将位置 标记化 个人文件夹。
  • 接下来,您必须从结果中删除重复项。*
  • 第三步将文件夹排列成层次结构 并将文档重新附加到它们的文件夹中。

这是您可以用作样式表基础的草图:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="location-by-path" match="location" use="@path" />
<xsl:key name="location-by-parent" match="location" use="@parent-path" />
<xsl:key name="document-by-location" match="row" use="location" />

<xsl:variable name="xml" select="/" />

<!-- 1st pass: tokenize paths to individual locations -->
<xsl:variable name="locations">
    <xsl:for-each select="/list/row">
        <xsl:call-template name="tokenize">
            <xsl:with-param name="text" select="location"/>
        </xsl:call-template>
    </xsl:for-each>
</xsl:variable>

<!-- 2nd pass: distinct locations only -->
<xsl:variable name="distinct-locations">
    <xsl:copy-of select="exsl:node-set($locations)/location[count(. | key('location-by-path', @path)[1]) = 1]"/>
</xsl:variable> 
<xsl:variable name="distinct-locations-set" select="exsl:node-set($distinct-locations)" />

<!-- output -->
<xsl:template match="/list">
    <root>
        <!-- start with progenitor locations  -->
        <xsl:apply-templates select="$distinct-locations-set/location[@parent-path='']"/>
    </root>
</xsl:template>

<xsl:template match="location">
    <xsl:variable name="path" select="@path" />
    <xsl:element name="{@name}">
        <!-- set context to XML input  -->
        <xsl:for-each select="$xml">
            <!-- get documents  -->
            <xsl:apply-templates select="key('document-by-location', $path)"/>
        </xsl:for-each>
        <!-- set context to distinct locations  -->
        <xsl:for-each select="$distinct-locations-set">
            <!-- get subdirectories  -->
            <xsl:apply-templates select="key('location-by-parent', concat($path, '\'))"/>
        </xsl:for-each>
    </xsl:element>
</xsl:template>

<xsl:template match="row">
    <document name="{title}"/>
</xsl:template>

<xsl:template name="tokenize">
    <xsl:param name="text"/>
    <xsl:param name="parent-path"/>
    <xsl:param name="delimiter" select="'\'"/>
    <xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)" />
    <xsl:if test="$token">
        <location name="{$token}" path="{concat($parent-path, $token)}" parent-path="{$parent-path}"/>
    </xsl:if>
    <xsl:if test="contains($text, $delimiter)">
        <!-- recursive call -->
        <xsl:call-template name="tokenize">
            <xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
            <xsl:with-param name="parent-path" select="concat($parent-path, $token, $delimiter)"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>


</xsl:stylesheet> 

应用于您的示例输入的结果将是:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <rootfolder>
      <document name="Doc2"/>
      <folder1>
         <document name="Doc3"/>
         <folder1.1>
            <folder1.1.1>
               <document name="Doc1"/>
            </folder1.1.1>
         </folder1.1>
      </folder1>
      <folder2>
         <folder2.1>
            <folder2.1.1>
               <document name="Doc4"/>
            </folder2.1.1>
         </folder2.1>
      </folder2>
   </rootfolder>
</root>

(*)您需要熟悉 Muenchian grouping 才能理解此解决方案。