建立一条"路径"使用XSLT递归

时间:2012-04-12 10:00:49

标签: xml xslt recursion

我有一个问题,目前无意解决它; - (

我有一个类别结构作为输入文档(xml),并且想要构建一个路径结构。 我只能使用xslt并希望生成一个新的xml结构。

输入结构如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<Positions>
    <Positionen>
        <ID>1</ID>
        <Parent></Parent>
    </Positionen>

    <Positionen>
        <ID>2</ID>
        <Parent>1</Parent>
    </Positionen>

    <Positionen>
        <ID>3</ID>
        <Parent>1</Parent>
    </Positionen>

    <Positionen>
        <ID>4</ID>
        <Parent>2</Parent>
    </Positionen>

    <Positionen>
        <ID>5</ID>
        <Parent>4</Parent>
    </Positionen>

    <Positionen>
        <ID>6</ID>
        <Parent>2</Parent>
    </Positionen>

</Positions>

输出结构应为:

<?xml version="1.0" encoding="UTF-8"?>
<Positions>
    <Positionen>
        <ID>1</ID>
        <Parent></Parent>
        <Path>1</Path>
    </Positionen>

    <Positionen>
        <ID>2</ID>
        <Parent>1</Parent>
        <Path>1/2</Path>
    </Positionen>

    <Positionen>
        <ID>3</ID>
        <Parent>1</Parent>
        <Path>1/3</Path>
    </Positionen>

    <Positionen>
        <ID>4</ID>
        <Parent>2</Parent>
        <Path>1/2/4</Path>
    </Positionen>

    <Positionen>
        <ID>5</ID>
        <Parent>4</Parent>
        <Path>1/2/4/5</Path>
    </Positionen>

    <Positionen>
        <ID>6</ID>
        <Parent>2</Parent>
        <Path>1/2/6</Path>
    </Positionen>

</Positions>

如何使用递归的xslt执行此操作? 希望得到一些帮助。提前致谢。 LStrike

2 个答案:

答案 0 :(得分:3)

这个问题的一些答案很好,但效率很低(O(N ^ 2))。

就是这样,因为路径是从头开始为每个Positionen元素构造的。平均路径长度为N / 2,并且存在N Positionen个元素。这意味着构建所有路径需要N * N / 2个操作作为最小值 - 这是二次时间复杂度。

这是一个更有效的O(N * log(N)) - 可以是偶数(O(N) - 线性)解决方案,以防它可以接受Positionen元素输出未分类

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

 <xsl:key name="kChildren" match="Positionen" use="Parent"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
    <xsl:apply-templates select="node()|@*"/>
  </xsl:variable>

  <xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/>

  <xsl:apply-templates select="$vPass1/*" mode="pass2"/>
 </xsl:template>

 <xsl:template match="/*">
  <xsl:copy>
    <xsl:apply-templates select="Positionen[not(number(Parent))]" mode="path"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="Positionen" mode="path">
  <xsl:param name="pPath"/>

  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
    <Path><xsl:value-of select="concat($pPath, ID)"/></Path>
  </xsl:copy>
  <xsl:apply-templates select="key('kChildren', ID)" mode="path">
   <xsl:with-param name="pPath" select="concat($pPath, ID, '/')"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="/*" mode="pass2">
  <xsl:copy>
       <xsl:apply-templates select="node()|@*">
         <xsl:sort select="ID" data-type="number"/>
       </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

将此转换应用于提供的XML文档

<Positions>
    <Positionen>
        <ID>1</ID>
        <Parent></Parent>
    </Positionen>
    <Positionen>
        <ID>2</ID>
        <Parent>1</Parent>
    </Positionen>
    <Positionen>
        <ID>3</ID>
        <Parent>1</Parent>
    </Positionen>
    <Positionen>
        <ID>4</ID>
        <Parent>2</Parent>
    </Positionen>
    <Positionen>
        <ID>5</ID>
        <Parent>4</Parent>
    </Positionen>
    <Positionen>
        <ID>6</ID>
        <Parent>2</Parent>
    </Positionen>
</Positions>

产生了想要的正确结果:

<Positions>
   <Positionen>
      <ID>1</ID>
      <Parent/>
      <Path>1</Path>
   </Positionen>
   <Positionen>
      <ID>2</ID>
      <Parent>1</Parent>
      <Path>1/2</Path>
   </Positionen>
   <Positionen>
      <ID>3</ID>
      <Parent>1</Parent>
      <Path>1/3</Path>
   </Positionen>
   <Positionen>
      <ID>4</ID>
      <Parent>2</Parent>
      <Path>1/2/4</Path>
   </Positionen>
   <Positionen>
      <ID>5</ID>
      <Parent>4</Parent>
      <Path>1/2/4/5</Path>
   </Positionen>
   <Positionen>
      <ID>6</ID>
      <Parent>2</Parent>
      <Path>1/2/6</Path>
   </Positionen>
</Positions>

请注意

通过将当前ID添加到父路径(仅计算一次) - O(1)操作来生成每个路径。总共有N条路径,这是O(N)。

最后的排序使时间复杂度为O(N * log(N)) - 仍然比二次更好。

答案 1 :(得分:2)

试试这个XSLT

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

   <xsl:key name="Pos" match="Positionen" use="ID" />

   <!-- Match Positionen elements normally -->
   <xsl:template match="Positionen">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
         <Path>
            <!-- Get parent path -->
            <xsl:apply-templates select="key('Pos', Parent)" mode="parent" />
            <!-- End of path -->
            <xsl:value-of select="ID" />
         </Path>
      </xsl:copy>
   </xsl:template>

   <!-- Template used to recursively match parents -->
   <xsl:template match="Positionen" mode="parent">
      <xsl:apply-templates select="key('Pos', Parent)" mode="parent" />
      <xsl:value-of select="concat(ID, '/')" />
   </xsl:template>

   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

首先匹配 Positionen 元素,然后根据 Parent 值递归匹配父元素。请注意,它还使用 xsl:key 来更快地按 ID 查找元素。

当应用于您的示例XML时,输出以下内容:

<Positions>
   <Positionen>
      <ID>1</ID>
      <Parent/>
      <Path>1</Path>
   </Positionen>
   <Positionen>
      <ID>2</ID>
      <Parent>1</Parent>
      <Path>1/2</Path>
   </Positionen>
   <Positionen>
      <ID>3</ID>
      <Parent>1</Parent>
      <Path>1/3</Path>
   </Positionen>
   <Positionen>
      <ID>4</ID>
      <Parent>2</Parent>
      <Path>1/2/4</Path>
   </Positionen>
   <Positionen>
      <ID>5</ID>
      <Parent>4</Parent>
      <Path>1/2/4/5</Path>
   </Positionen>
   <Positionen>
      <ID>6</ID>
      <Parent>2</Parent>
      <Path>1/2/6</Path>
   </Positionen>
</Positions>