根据属性从列表创建层次结构

时间:2013-11-16 07:52:43

标签: xml xslt xpath

我一直在尝试根据属性计数将此列表放在层次结构上,但我无法做到。起初是尝试在一个阶段,分阶段进行,但在使用previous-sibling和follow-sibling时我一直遇到同样的问题。

这就是我所拥有的

<body>
    <element count="2"/>
    <element count="2"/> 
    <element count="2"/> 
    <element count="2"/>
    <element count="4"/>
    <element count="4"/>
    <element count="6"/>
    <element count="4"/>
    <element count="2"/>
    <element count="4"/>
    <element count="2"/>
    <element count="4"/>
    <element count="6"/>
    <element count="6"/>
    <element count="4"/>
    <element count="2"/> 
 </body>

这就是我想要的

<body>
    <element count="2"/>
    <element count="2"/> 
    <element count="2"/> 
    <element count="2">
        <element count="4"/>
        <element count="4">
            <element count="6"/>
        </element>
        <element count="4"/>
    </element>
    <element count="2">
        <element count="4"/>
    </element>
    <element count="2">
        <element count="4">
            <element count="6"/>
            <element count="6"/>
        </element>
        <element count="4"/>
    </element> 
    <element count="2"/> 
 </body>

任何帮助将不胜感激,谢谢!

2 个答案:

答案 0 :(得分:1)

如果您知道count属性的值分别为2,4,6,..,那么您就知道初始计数和计数之间的差异,这很容易:

<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs mf">

<xsl:output indent="yes"/>

<xsl:function name="mf:group" as="element(element)*">
  <xsl:param name="elements" as="element(element)*"/>
  <xsl:param name="level" as="xs:integer"/>
  <xsl:param name="step" as="xs:integer"/>
  <xsl:for-each-group select="$elements" group-starting-with="element[@count = $level]">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:sequence select="mf:group(current-group() except ., $level + $step, $step)"/>
    </xsl:copy>  
  </xsl:for-each-group>
</xsl:function>

<xsl:template match="body">
  <xsl:copy>
    <xsl:sequence select="mf:group(element, 2, 2)"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

答案 1 :(得分:0)

这是一个可能的XSLT 1.0解决方案。这里的诀窍是使用将每个element链接到最近的前一个兄弟,该兄弟的count值非常小。

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

  <xsl:key name="childrenByParent" match="element"
    use="generate-id(preceding-sibling::element[@count &lt; current()/@count][1])" />

  <xsl:template match="/body">
    <body>
      <xsl:apply-templates select="key('childrenByParent', '')" />
    </body>
  </xsl:template>

  <xsl:template match="element">
    <element>
      <xsl:copy-of select="@*" />
      <xsl:apply-templates select="key('childrenByParent', generate-id())" />
    </element>
  </xsl:template>
</xsl:stylesheet>

起点key('childrenByParent', '')有效,因为我们需要从没有“父”的元素开始,即preceding-sibling::element[@count &lt; current()/@count][1]选择空节点的元素设置,以及空节点集is defined to be the empty stringgenerate-id

如果元素可以被命名为任何内容,只要它们具有count属性,您就会在评论中询问需要更改的内容。在这种情况下,您只需要对键定义进行一些小改动,使其(a)匹配任何带count的元素,(b)查找带有 any 的父元素名称而不仅仅是element

  <xsl:key name="childrenByParent" match="*[@count]"
    use="generate-id(preceding-sibling::*[@count &lt; current()/@count][1])" />

并向第二个模板提供更通用的match模式,并使用xsl:copy代替硬编码元素名称来创建:

  <xsl:template match="*">
    <xsl:copy>
      <xsl:copy-of select="@*" />
      <xsl:apply-templates select="key('childrenByParent', generate-id())" />
    </xsl:copy>
  </xsl:template>