【问题标题】:Is it possible to fetch only a specific branch of a Hierarchy using XSLT?是否可以使用 XSLT 仅获取层次结构的特定分支?
【发布时间】:2021-04-18 09:28:51
【问题描述】:

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

例如

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>

【问题讨论】:

  • 请发布minimal reproducible example 显示示例输入(作为 XML 代码)、预期输出(也作为代码)和您当前的尝试。 -- 提示:如果你只传递在descendant-or-self 轴上具有“Shift”的节点,这将相当简单。
  • 我喜欢这个问题。本质上,您希望找到具有指定数据值的节点。然后你想得到那个节点,以及它的所有祖先(即父母、祖父母、曾祖父母等)。所以,这是一种方法:找到指定的数据点。使用祖先或自我轴创建一个节点集,然后以正确的顺序输出节点集...如果您需要更多帮助,请按照迈克尔上面的建议修改您的问题...
  • @Bluewood66 您的方法将为兄弟节点创建重复的分支。
  • @michael.hor257k 例如,如果我找到“1.2.2 Shift”节点,则获取其父节点“1.2 Transmission”,然后获取其祖父节点“1. Audi”。那会奏效。同样,如果获得“2.2.2 Shift”,则父“2.2 Transmission”的祖父“2. Ferrari”也可以使用。所以,只要我从带有“Shift”的子节点开始,我可能就很好。该方法是自下而上,而不是自上而下......但是,请让我知道如何创建重复的分支。我认为自上而下的方法会有更多问题。
  • @Bluewood66 我会在 OP 发布他的 XML 代码时澄清这一点 - 但请考虑如果制造商有两个部门会发生什么。你的结果的根元素是什么。

标签: xml xslt xslt-1.0


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    在对您的问题的评论中,我说这是相当微不足道的。那是因为我认为您的输入 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>
    

    【讨论】:

      猜你喜欢
      • 2021-04-22
      • 2021-04-12
      • 1970-01-01
      • 1970-01-01
      • 2018-07-23
      • 2018-08-25
      • 1970-01-01
      • 2020-06-24
      • 2012-05-29
      相关资源
      最近更新 更多