从 XSLT 中的路径创建嵌套树结构 [英] Creating a nested tree structure from a path in XSLT

查看:27
本文介绍了从 XSLT 中的路径创建嵌套树结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个动态 XML 文档,它表示类别的树结构,但使用路径分隔属性以任意顺序 - 像这样:

I have a dynamic XML document which represents a tree structure of categories, but does so using path separated attributes in arbitrary order - like this:

   <data>    
      <record ID="24" Name="category 1\sub category 1"/>   
      <record ID="26" Name="category 1"/>     
      <record ID="25" Name="category 1\sub category 1\sub category 2"/>    
      <record ID="27" Name="category 1\sub category 1\sub category 3"/>    
      ...
   </data> 

我需要提出一个规范化"XML 的解决方案,以便将其转换为如下所示的内容:

I need to come up with a solution that 'normalizes' the XML so that it is transformed into something like this:

   <data>    
      <record ID="26" Name="category 1">    
         <record ID="24" Name="sub category 1">    
            <record ID="25" Name="sub category 2"/>
            <record ID="27" Name="sub category 3"/>    
         </record>
      </record>   
      ...
   </data>

基本上,我想知道这是否是 XSLT 可能能够解决的问题,以及如何解决,而不必以编程方式进行.

Basically I was wondering if this is something XSLT might be able to address, and how, rather than having to do it programmatically.

推荐答案

好的,没问题:

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

  <xsl:output indent="yes" />

  <xsl:template match="/data">
    <!-- copy the document element -->
    <xsl:copy>
      <!-- That's where we start: all "record" nodes that have no "\". -->
      <xsl:apply-templates mode="recurse" select="/data/record[
        not(contains(@Name, '\'))
      ]" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="record" mode="recurse">
    <xsl:param name="starting-path" select="''" />

    <!-- The record node and its ID attribute can be copied. --> 
    <xsl:copy>
      <xsl:copy-of select="@ID" />

      <!-- Create the new "name" attribute. -->
      <xsl:attribute name="Name">
        <xsl:value-of select="substring-after(@Name, $starting-path)" />
      </xsl:attribute>

      <!-- Append a backslash to the current path. -->
      <xsl:variable name="current-path" select="concat(@Name, '\')" />

      <!-- Select all "record" nodes that are one level deeper... -->
      <xsl:variable name="children" select="/data/record[
        starts-with(@Name, $current-path)
        and
        not(contains(substring-after(@Name, $current-path), '\'))
      ]" />

      <!-- ...and apply this template to them. -->
      <xsl:apply-templates mode="recurse" select="$children">
        <xsl:with-param name="starting-path" select="$current-path" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

我系统上的输出:

<data>
  <record ID="26" Name="category 1">
    <record ID="24" Name="sub category 1">
      <record ID="25" Name="sub category 2"></record>
      <record ID="27" Name="sub category 3"></record>
    </record>
  </record>
</data>

请注意,整个解决方案基于以下假设:所有路径都是规范的且不包含尾部反斜杠.

Note that the whole solution is based on the assumption that all the paths are canonical and do not contain trailing backslashes.

另请注意,任何不匹配/孤立的记录"元素都不会出现在输出中(当然,除非它们位于根级别).

Also note that any unmatched/orphaned "record" elements will not be in the output (unless they are at the root level, of course).

还有一点:模板模式(递归")并不是绝对必要的.我包含它是因为模板正在做一些非常特别的事情,并且您的解决方案中可能有另一个模板匹配记录"节点.在这种情况下,可以在不破坏其他任何东西的情况下插入此解决方案.对于独立解决方案,可以随时删除模板模式.

One more thing: The template mode ("recurse") is not strictly necessary. I included it because the template is doing something rather special, and there might be the chance that there is another template in your solution that matches "record" nodes. In this case this solution can be dropped in without breaking anything else. For a standalone solution, the template modes can be dropped anytime.

哦,最后一件事:如果您希望按名称对结果文档进行排序,请包含 <xsl:sort> 元素(都出现),像这样:

Oh, and the last thing: If you want the result document to be ordered by Name, include an <xsl:sort> element with the <xsl:apply-templates> (both occurrences), like so:

<xsl:apply-templates select="...">
  <xsl:sort select="@Name" data-type="text" order="ascending" />
</xsl:apply-templates>

这篇关于从 XSLT 中的路径创建嵌套树结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆