【问题标题】:Deepening XSLT Structure using Attribute as Key使用属性作为键深化 XSLT 结构
【发布时间】:2012-08-24 19:50:11
【问题描述】:

我在这里看到了这个问题的一些变体,但我不确定如何将它们应用于我的情况,所以我希望也许有人可以在这里帮助我。

我有一个格式与此类似的平面 XML 文件:

<item id="1"/>
<item id="1.1"/>
<item id="1.1.1"/>
<item id="1.1.2"/>
<item id="1.1.2.1"/>
<item id="1.2"/>
<item id="1.3"/>

我希望根据 id 属性分层设置标签,如下所示:

<item id="1">
  <item id="1.1">
    <item id="1.1.1"/>
    <item id="1.1.2">
      <item id="1.1.2.1"/>
    </item>
  </item>
  <item id="1.2"/>
  <item id="1.3"/>
</item>

一些 id 值有两位数字(例如,“1.2.3.15.1”),这使得比较它们更具挑战性。

帮助?

【问题讨论】:

  • 你的终止条件是什么。您需要做的是,有一个函数以递归方式调用自身,其值如 1,1.1,1.2,1.3.. 1.nnn 然后,对于它们中的每一个,再次调用 1.1.1、1.1.2 等。每个系列必须以您预定义的条件结束。
  • 一种优化可以是首先收集所有属性,按您需要的顺序对它们进行排序并仅调用这些属性,因此您的终止不是硬编码的。..

标签: xslt xslt-1.0 xslt-2.0 iterative-deepening


【解决方案1】:

选择正确的节点可能很棘手,但由于您没有层次结构,这适用于您的示例输入(如果您向其中添加根元素)

  <!-- start somewhere -->
  <xsl:template match="/root">
    <root>
      <!-- select all with no . in the id -->
      <xsl:apply-templates  select="//item[string-length(translate(@id,'1234567890',''))=0]" />
    </root>
  </xsl:template>

  <xsl:template match="item">
    <xsl:variable name="id" select="@id"/>
    <!-- how many . have we ? That is how deep we are and add 1 -->
    <xsl:variable name="deep" select="string-length(translate(@id,'1234567890',''))+1" />
    <xsl:copy>
      <!-- copy attribs over -->
      <xsl:apply-templates select="@*"/> 
      <!-- select all nodes that start with our curent id,  
           select nodes that are just one level below us 
           and don't select our selfs-->
      <xsl:apply-templates select="//item[starts-with(@id, $id) and string-length(translate(@id,'1234567890',''))=$deep and not(@id=$id)]"/>
    </xsl:copy>
  </xsl:template>

  <!-- copy attribs -->
  <xsl:template match="@*">
    <xsl:copy />
  </xsl:template>

【讨论】:

  • 谢谢。这个成功了,尽管我必须纠正一个方面。输入文件有一些带有两位数字的 ID(例如 1.2.3.11),这导致节点选择失败。我通过将 ID 格式化为两位数(例如 01.02.03.11)作为第一次转换来解决此问题。
【解决方案2】:

我。这是一个简单的 XSLT 2.0 解决方案(在此之后还有一个类似的 XSLT 1.0 解决方案):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:my="my:my">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
     <xsl:sequence select="my:grouping(*, 1)"/>
 </xsl:template>

 <xsl:function name="my:grouping" as="element()*">
  <xsl:param name="pNodes" as="element()*"/>
  <xsl:param name="pLevel" as="xs:integer"/>

  <xsl:if test="$pNodes">
      <xsl:for-each-group select="$pNodes" group-by="tokenize(@id, '\.')[$pLevel]">
       <xsl:copy>
         <xsl:copy-of select="@*"/>
         <xsl:sequence select="
          my:grouping(current-group()[tokenize(@id, '\.')[$pLevel+1]], $pLevel+1)"/>
       </xsl:copy>
  </xsl:for-each-group>
  </xsl:if>
 </xsl:function>
</xsl:stylesheet>

在此 XML 文档上应用此转换时(提供的 XML 片段,包装在单个顶部元素中以使其成为格式良好的 XML 文档):

<t>
    <item id="1"/>
    <item id="1.1"/>
    <item id="1.1.1"/>
    <item id="1.1.2"/>
    <item id="1.1.2.1"/>
    <item id="1.2"/>
    <item id="1.3"/>
</t>

产生想要的正确结果

<item id="1">
   <item id="1.1">
      <item id="1.1.1"/>
      <item id="1.1.2">
         <item id="1.1.2.1"/>
      </item>
   </item>
   <item id="1.2"/>
   <item id="1.3"/>
</item>

二。这是一个类似的 XSLT 1.0 解决方案

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kFollowing" match="item"
   use="generate-id(preceding-sibling::*
                     [string-length(current()/@id) > string-length(@id)
                    and
                      starts-with(current()/@id, concat(@id, '.'))]
                       [1])"/>

 <xsl:template match="/*">
     <xsl:call-template name="grouping">
      <xsl:with-param name="pNodes" select="*"/>
      <xsl:with-param name="pLevel" select="1"/>
     </xsl:call-template>
 </xsl:template>

 <xsl:template name="grouping">
  <xsl:param name="pNodes"/>
  <xsl:param name="pLevel" select="1"/>

  <xsl:for-each select=
    "$pNodes[$pLevel > string-length(@id) - string-length(translate(@id, '.', ''))]">
   <xsl:copy>
     <xsl:copy-of select="@*"/>

     <xsl:call-template name="grouping">
       <xsl:with-param name="pNodes" select="key('kFollowing', generate-id())"/>
         <xsl:with-param name="pLevel" select="$pLevel+1"/>
     </xsl:call-template>
   </xsl:copy>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

当这个 XSLT 1.0 转换应用于同一个文档(上图)时,会产生同样想要的正确结果

<item id="1">
   <item id="1.1">
      <item id="1.1.1"/>
      <item id="1.1.2">
         <item id="1.1.2.1"/>
      </item>
   </item>
   <item id="1.2"/>
   <item id="1.3"/>
</item>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-15
    • 1970-01-01
    • 1970-01-01
    • 2012-06-22
    • 1970-01-01
    • 1970-01-01
    • 2019-04-27
    相关资源
    最近更新 更多