是否可以使用 XSLT 仅获取层次结构的特定分支?

Is it possible to fetch only a specific branch of a Hierarchy using XSLT?

假设我有一个汽车公司不同部分的层次结构。

例如

1. Audi
    1.1 Engine
            1.1.1 Piston
            1.1.2 Crankshaft
    1.2 Transmission
            1.2.1 Gears
            1.2.2 Shift

2. Ferrari
    2.1 Engine
            2.1.1 Piston
            2.1.2 Crankshaft
    2.2 Transmission
            2.2.1 Gears
            2.2.2 Shift
    

3. Ford
    3.1 Engine
            3.1.1 Piston
            3.1.2 Crankshaft
    3.2 Transmission
            3.2.1 Gears

现在输出必须只包含其中包含“Shift”的层次结构。其余部分一定不能在我的输出中看到。输出应如下所示:

  1. 奥迪 1.2 传输 1.2.2 移位

  2. 法拉利 2.2 传输 2.2.2 移位

是否有任何特定元素可用于获取此输出?

更新示例XML 输入文件

<?xml version="1.0" encoding="UTF-8"?>
<Hierarchy>
    <Board>
        <Name>Audi</Name>
        <Id>ABCDE</Id>
        <ParentId></ParentId>
        <General>
            <Name>Audi</Name>
            <Description>Car brand</Description>
            <Template>LEVEL1</Template>
        </General>
    </Board>
    <Board>
        <Name>Engine</Name>
        <Id>EFGHI</Id>
        <ParentId>ABCDE</ParentId>
        <General>
            <Name>Engine</Name>
            <Description>Part of Car</Description>
            <Template>LEVEL2</Template>
        </General>
    </Board>
    <Board>
        <Name>Piston</Name>
        <Id>JKLMN</Id>
        <ParentId>EFGHI</ParentId>
        <General>
            <Name>Department_Heads</Name>
            <Description>Part of Engine</Description>
            <Template>LEVEL3</Template>
        </General>
    </Board>
    <Board>
        <Name>Crankshaft</Name>
        <Id>OPQRS</Id>
        <ParentId>EFGHI</ParentId>
        <General>
            <Name>Department_Heads</Name>
            <Description>Part of Engine</Description>
            <Template>LEVEL3</Template>
        </General>
    </Board>
    <Board>
        <Name>Transmission</Name>
        <Id>TUVWX</Id>
        <ParentId>ABCDE</ParentId>
        <General>
            <Name>Transmission</Name>
            <Description>Part of Car</Description>
            <Template>LEVEL2</Template>
        </General>
    </Board>
    <Board>
        <Name>Gear</Name>
        <Id>CSDKL</Id>
        <ParentId>TUVWX</ParentId>
        <General>
            <Name>Gear</Name>
            <Description>Part of Transmission</Description>
            <Template>LEVEL3</Template>
        </General>
    </Board>
    <Board>
        <Name>Shift</Name>
        <Id>SDKLFH</Id>
        <ParentId>TUVWX</ParentId>
        <General>
            <Name>Shift</Name>
            <Description>Part of Transmission</Description>
            <Template>LEVEL3</Template>
        </General>
    </Board>
</Hierarchy>

我采用的 XSLT 方法是这样的:

    <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <Hierarchy>
            <xsl:for-each select="Hierarchy/Board">

                <xsl:if test="General/Template='LEVEL1'">
                    <xsl:variable name="blabla" select="Id"/>
                    <Board>
                        
                        <Name>
                            <xsl:value-of select="Name"/>
                        </Name>
                        <Template>CAR1</Template>
                        <Description>
                            <xsl:value-of select = "General/Description"/>
                        </Description>
                        <xsl:for-each select="//Board">
                            <xsl:if test="ParentId = $blabla">
                            
                                <Board>
                                    <xsl:variable name="blablablablabla" select="Id"/>
                                    
                                    <Name>
                                        <xsl:value-of select="Name"/>
                                    </Name>
                                    <Template>TRANSMISSION</Template>
                                    <Description>
                                        <xsl:value-of select = "General/Description"/>
                                    </Description>
                                    <xsl:for-each select="//Board">
                                        <xsl:if test="ParentId = $blablablablabla">
                                        
                                            <Board>
                                                <xsl:variable name="blablablablablablablablablablablabla" select="Id"/>
                                                
                                                <Name>
                                                    <xsl:value-of select="Name"/>
                                                </Name>
                                                <Template>SHIFT</Template>
                                                <Description>
                                                    <xsl:value-of select = "General/Description"/>
                                                </Description>
                                            </Board>
                                        </xsl:if>
                                    </xsl:for-each>
                                </Board>
                            </xsl:if>
                        </xsl:for-each>
                        
                    </Board>
                </xsl:if>
            </xsl:for-each>
                      
        </Hierarchy>
    </xsl:template>
</xsl:stylesheet>

请注意,我刚才只提到了整个 XSLT 的特定部分。通常当我使用它时它工作正常,但是当数据增加时,就像上面的 50 万个元素,并且当“Shift”处于不同级别时(这里它在层次结构树中处于第 3 级),出于某种原因它创建重复以及其中没有任何模板的元素。我只想知道有没有其他的办法

输出XML样本也在下面提到:

    <?xml version="1.0" encoding="utf-8"?>
    <Hierarchy>
      <Board>
        <Name>Audi</Name>
        <Template>LEVEL1</Template>
        <Description>Car brand</Description>
         <Board>
          <Name>Transmission</Name>
          <Template>LEVEL2</Template>
          <Description>Part of Car</Description>
           <Board>
           <Name>Shift</Name>
           <Template>LEVEL3</Template>
            <Description>Part of Transmission</Description>
          
          </Board>
        </Board>

   </Board>
   <Board>
        <Name>Ferrari</Name>
        <Template>LEVEL1</Template>
        <Description>Car brand</Description>
         <Board>
          <Name>Transmission</Name>
          <Template>LEVEL2</Template>
          <Description>Part of Car</Description>
           <Board>
           <Name>Shift</Name>
           <Template>LEVEL3</Template>
            <Description>Part of Transmission</Description>
          
          </Board>
        </Board>

   </Board>
</Hierarchy>

@Onlyfor Surfing 尝试将此代码添加到您的样式表中。您可以根据需要对其进行平滑处理。

<xsl:key name="myKey" match="Board" use="Id"/>

<xsl:variable name="root" select="/"/>

<!-- I would suggest working out the hierarchy first as follows: -->
<xsl:variable name="metadata">
  <!-- Change the Board filter to be whatever level 3 starting nodes that you want. 
       You could just use level 3, if you want.  Or an attribute.
    -->
  <xsl:apply-templates select="//Board[contains(Name, 'Shift') or contains(Name, 'Gear')]" mode="metadata"/> 
</xsl:variable>

<xsl:variable name="metadataList" select="msxml:node-set($metadata)"/>

<xsl:template match="Hierarchy">
  <xsl:copy>
    <xsl:for-each select="$metadataList/element">
      <xsl:variable name="position" select="position()"/>
      <xsl:variable name="firstId" select="Id[1]"/>
      <xsl:for-each select="$root">
        <xsl:apply-templates select="key('myKey', $firstId)">
          <xsl:with-param name="Id" select="$firstId"/>
          <xsl:with-param name="position" select="$position"/>
        </xsl:apply-templates>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>

<xsl:template match="Board">
  <xsl:param name="Id"/>
  <xsl:param name="position"/>
  <xsl:copy>
    <xsl:apply-templates select="Name"/>
    <xsl:apply-templates select="General/Template"/>
    <xsl:apply-templates select="General/Description"/>
    <xsl:variable name="followingId" select="$metadataList/element[number($position)]/Id[. = $Id]/following-sibling::Id[1]"/>
    <xsl:if test="$followingId != ''">
      <xsl:apply-templates select="key('myKey', $followingId)">
        <xsl:with-param name="Id" select="$followingId"/>
        <xsl:with-param name="position" select="$position"/>
      </xsl:apply-templates>
    </xsl:if>
  </xsl:copy>
</xsl:template>
 
<xsl:template match="Name">
  <xsl:copy-of select="."/>
</xsl:template>

<xsl:template match="Template">
  <xsl:copy-of select="."/>
</xsl:template>

<xsl:template match="Description">
  <xsl:copy-of select="."/>
</xsl:template>


<!-- metadata mode -->
<xsl:template match="Board" mode="metadata">
  <xsl:element name="element">
    <xsl:if test="ParentId != ''">
      <xsl:apply-templates select="key('myKey', ParentId)" mode="metadata2">
        <xsl:with-param name="ParId" select="ParentId"/>
      </xsl:apply-templates>
    </xsl:if>
    <xsl:element name="Id">
      <xsl:value-of select="Id"/>
    </xsl:element>
  </xsl:element>
</xsl:template>

<!-- metadata2 mode -->
<xsl:template match="Board" mode="metadata2">
  <xsl:if test="ParentId != ''">
    <xsl:apply-templates select="key('myKey', ParentId)" mode="metadata2">
      <xsl:with-param name="ParId" select="ParentId"/>
    </xsl:apply-templates>
  </xsl:if>
  <xsl:element name="Id">
    <xsl:value-of select="Id"/>
  </xsl:element>
</xsl:template>

<!-- Iterate thru all node.  -->
<xsl:template match="node()">
    <xsl:apply-templates select="node()"/>
</xsl:template>

在对您的问题的评论中,我说这是相当微不足道的。那是因为我认为您的输入 XML 具有层次结构。事实证明,需要通过跟踪从每个 Board 到其父级的交叉引用来派生层次结构。

这意味着转换需要分两步进行:首先,构建适当的层次树;然后修剪这棵树,只留下结果子的树枝。

XSLT 1.0(+ EXSLT 节点集功能)

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="children" match="Board" use="ParentId" />

<xsl:template match="/Hierarchy">
    <!-- first pass -->
    <xsl:variable name="tree">
        <xsl:apply-templates select="Board[General/Template='LEVEL1']" mode="first-pass"/>
    </xsl:variable>
    <!-- output -->
    <xsl:copy>
        <xsl:apply-templates select="exsl:node-set($tree)"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="Board" mode="first-pass">
    <xsl:copy>
        <xsl:copy-of select="*"/>
        <xsl:apply-templates select="key('children', Id)" mode="first-pass"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="Board">
    <xsl:copy>
        <xsl:copy-of select="General/*"/>
        <xsl:apply-templates select="Board[descendant-or-self::Board/Name='Shift']"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

当应用于提供的输入 XML 时,这将产生:

结果

<?xml version="1.0" encoding="UTF-8"?>
<Hierarchy>
  <Board>
    <Name>Audi</Name>
    <Description>Car brand</Description>
    <Template>LEVEL1</Template>
    <Board>
      <Name>Transmission</Name>
      <Description>Part of Car</Description>
      <Template>LEVEL2</Template>
      <Board>
        <Name>Shift</Name>
        <Description>Part of Transmission</Description>
        <Template>LEVEL3</Template>
      </Board>
    </Board>
  </Board>
</Hierarchy>