【问题标题】:Create hierarchy from list based on attribute根据属性从列表创建层次结构
【发布时间】:2013-11-16 07:52:42
【问题描述】:

我一直试图根据属性计数将此列表放在层次结构中,但我无法正确。起初试图分阶段完成所有工作,但在使用前同级和后同级时我一直遇到同样的问题。

这就是我所拥有的

<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>

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

【问题讨论】:

  • 看看这个stackoverflow.com/questions/19884129。 @MichaelKay 在那里解决了一个非常相似的问题。您实际上可以跳过一些步骤,因为缩进级别已经在您的设置中明确给出。

标签: xml xslt xpath


【解决方案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>

【讨论】:

    【解决方案2】:

    这是一个可能的 XSLT 1.0 解决方案。这里的诀窍是使用 key 将每个 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 &amp;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>
    

    【讨论】:

    • 嗯,我使用 Xalan 2.7 开发了这个样式表,它在那里工作,以及在 Saxon 9 上工作,但我刚刚使用 xsltproc 对其进行了很好的测试,并且在那里 不起作用。看来不同的处理器对current()在一个键的use表达式中是否合法有不同的看法,所以YMMV。
    • 如果元素不叫元素,但可以是任何东西,只是需要有count属性会怎样?
    • @DavidMendezGonzalez 这只是几个小改动,我已经用细节编辑了我的答案。
    • 它可以工作,但它不会复制元素内的文本,并且由于某种原因它在第一级添加了命名空间作为属性。
    • @DavidMendezGonzalez 问题中没有任何元素任何要复制的文本...如果您需要这个,您必须将“| text()”添加到副本或应用模板。同样,问题中也没有命名空间。
    猜你喜欢
    • 1970-01-01
    • 2019-02-13
    • 2022-01-27
    • 2023-04-06
    • 2014-12-08
    • 2018-11-19
    • 1970-01-01
    • 2013-06-07
    • 1970-01-01
    相关资源
    最近更新 更多