在封闭元素下排列节点 - XSLT

Posted

技术标签:

【中文标题】在封闭元素下排列节点 - XSLT【英文标题】:Arrange nodes under a closed element - XSLT 【发布时间】:2013-06-11 04:19:12 【问题描述】:

所以我有这个 XML:

<Main>
 <TB>
    --> some elements - not relevant
   <Area>
     <Type>A</Type>
     <Street>
       <Position>5</Position>
       <House>

       --> some elements

       </House>
     </Street>
     <Street>
       <Position>5</Position>
       <Block>

       --> some elements

       </Block>
     </Street>
     <Street>
       <Position>6</Position>
       <House>

       --> some elements

       </House>
     </Street>
     <Street>
       <Position>6</Position>
       <Block>

       --> some elements

       </Block>
     </Street>
   </Area>
   <Area>
    <Type>B</Type>
   --> same structure but with several repetitions of Position 7 and 8.

   </Area>
 </TB>
</Main>

我想这样订购:

<Area>
   <Type>A</Type>
     <Street>
       <Position>5</Position>
       <House>

       --> some elements

       </House>
       <Block>

       --> some elements

       </Block>
     </Street>
     <Street>
       <Position>6</Position>
       <House>

       --> some elements

       </House>
       <Block>

       --> some elements

       </Block>
     </Street>
   </Area>
   <Area>
     <Type>B</Type>
   --> same structure for Position 7 and 8.

   </Area>

我正在使用这个 XSLT 对其进行转换:

<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="streetByPosition" match="Street" use="Position" />

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

  <!-- for the first Street in each Position -->
  <xsl:template match="Street[generate-id() =
                            generate-id(key('streetByPosition', Position)[1])]">
    <Street>
      <!-- copy in the Position element once only -->
      <xsl:apply-templates select="Position" />
      <!-- copy in all sub-elements except Position from all matching Streets-->
      <xsl:apply-templates select="
            key('streetByPosition', Position)/*[not(self::Position)]" />
    </Street>
  </xsl:template>

  <!-- ignore all other Street elements -->
  <xsl:template match="Street" />
</xsl:stylesheet>

排序工作完全正常。 但是,如果我在不同的Type 中有重复的Position 数字,那么我会将所有Houses 和Blocks 安排在第一个Type 中,其中我有重复的Position 数字。 例如:

<Area>
         <Type>A</Type>
         <Street>
           <Position>5</Position>
           <House>

           --> some elements

           </House>
         </Street>
         <Street>
           <Position>5</Position>
           <Block>

           --> some elements

           </Block>
         </Street>
....
<Area>
     <Type>B</Type>
     <Street>
       <Position>5</Position>
       <House>

       --> some elements

       </House>
     </Street>

然后Type BPosition 5下的元素将从那里移动到TypeAPosition 5下。我不希望那样。我希望安排房屋和街区,但要保留在自己的类型和区域中。

谁能为我提供如何更改我的 XSLT 才能解决此问题的解决方案?

附:为简化起见,更改了 XML 标记的名称。而且我不能使用 xslt-2.0,因为我的编辑器不支持它。

【问题讨论】:

【参考方案1】:

为此,您需要一个复合键,它将是标识您的组的所有值的串联,在您的情况下是 Type(父元素的),以及位置

<xsl:key name="streetByPosition" match="Street" use="concat(../Type, '|', Position)" />

然后就可以正常使用密钥了

key('streetByPosition', concat(../Type, '|', Position))

试试下面的 XSLT:

<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="streetByPosition" match="Street" use="concat(../Type, '|', Position)" />

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

  <!-- for the first Street in each Position -->
  <xsl:template match="Street[generate-id() =
                            generate-id(key('streetByPosition', concat(../Type, '|', Position))[1])]">
    <Street>
      <!-- copy in the Position element once only -->
      <xsl:apply-templates select="Position" />
      <!-- copy in all sub-elements except Position from all matching Streets-->
      <xsl:apply-templates select="
            key('streetByPosition', concat(../Type, '|', Position))/*[not(self::Position)]" />
    </Street>
  </xsl:template>

  <!-- ignore all other Street elements -->
  <xsl:template match="Street" />
</xsl:stylesheet>

唯一需要注意的是,串联中的“竖线”字符可以是任何你喜欢的字符,只要它不在TypePosition中的任何一个中出现即可 元素,以确保此类元素的两个不同组合不会产生相同的键。

【讨论】:

以上是关于在封闭元素下排列节点 - XSLT的主要内容,如果未能解决你的问题,请参考以下文章

如何获取具有相同名称的元素并根据 XSLT 中的子节点值对它们进行排序

XSLT:在重新排列 XML 时面临多个父节点的问题

使用 XSLT 将 XML 元素移动到不同的节点

使用 xslt 将 xml 复杂节点元素拆分为多个节点

如何将前缀类型添加到重复父节点并使用 XSLT 选择每个元素的所有元素?

XSLT Grouping's:将父级添加到子元素中的元素集