4

鉴于此 XML

<Xml>
    <Thing id="1"  >
        <Foo id="11" parentId="12"/>
        <Foo id="12"/>
    </Thing>
    <Thing id="2" parentId="1" />
    <Thing id="3" parentId="2" />
    <Thing id="4">
        <Foo id="11" parentId="15"/>
        <Foo id="12" parentId="14"/>
        <Foo id="13" parentId="11"/>
        <Foo id="14" parentId="15"/>
        <Foo id="15"/>      
    </Thing>
</Xml>

我想抓住每一个兄弟姐妹的集合,并将它们组装成自己的层次结构。

每个具有 parentId 值的“事物”节点都应该嵌套在相应的事物节点下。每个具有 parentId 值的“Foo”节点都应该嵌套在相应的 Foo 节点下——但只能嵌套在其兄弟节点内。该示例有两组 Foo 兄弟姐妹。

我正在尝试创建这个:

<Xml>
    <Thing id="1" >
        <Foo id="12">
            <Foo id="11" parentId="12"/>
        </Foo>        
        <Thing id="2" parentId="1" >
            <Thing id="3" parentId="2" />
        </Thing>
    </Thing>            
    <Thing id="4" >
        <Foo id="14" parentId="12">
            <Foo id="12" parentId="14"/>
        </Foo>
        <Foo id="15">
            <Foo id="11" parentId="15">
                <Foo id="13" parentId="11"/>
            </Foo>
        </Foo>
    </Thing>
</Xml>

这个例子很接近: 如何使用 XSLT 1.0 向非分层 XML 文件添加结构?

我使用标识模板来保留所有节点和属性。然后我想要一个覆盖模板来匹配所有具有兄弟姐妹(后面或前面)的节点,以便兄弟姐妹的 @parentId 值等于我的 @id 值。我最接近的方法是硬编码要匹配的 id/parentId 值。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output indent="yes"/>

    <!-- override identity rule with template to match on
        a node who has siblings, where sibling/@parentId == ./@id
    -->
    <xsl:template match="node()[@id='1' and (preceding-sibling::*[@parentId = 1] or following-sibling::*[@parentId = 1])]">
       <captured>
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
       </captured>
    </xsl:template>  


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


</xsl:stylesheet>

我看不到如何根据 parentId 值获取当前节点 @id 值以在 Xpath 匹配兄弟的谓词中使用。

然后我想在它下面嵌套当前节点兄弟姐妹,其中兄弟姐妹@ParentId 等于我的@id。

4

1 回答 1

4

首先请注意提供的 XML 文档中的错误

    <Foo id="12" parentId="14"/>
    <Foo id="13" parentId="11"/>
    <Foo id="14" parentId="12"/>

Foowith id12 和Foowith之间存在循环关系14。这形成了一个循环,而不是“层次结构”。此外,这两个Foo元素从层次结构的顶部是无法访问的!请正确

这种转变

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

 <xsl:key name="kElemById" match="*"
   use="concat(generate-id(..), '+', @id)"/>
 <xsl:key match="*" name="kDescendants"
  use="concat(generate-id(key('kElemById', 
                              concat(generate-id(..), '+',@parentId))),
             '+', @parentId)"/>

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

 <xsl:template match="/*">
  <Xml>
   <xsl:apply-templates select="*[not(@parentId)]"/>
  </Xml>
 </xsl:template>

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

    <xsl:apply-templates select="*[not(@parentId)]"/>

    <xsl:apply-templates select=
       "key('kDescendants', concat(generate-id(), '+',  @id))"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<Xml>
    <Thing id="1"  >
        <Foo id="11" parentId="12"/>
        <Foo id="12"/>
    </Thing>
    <Thing id="2" parentId="1" />
    <Thing id="3" parentId="2" />
    <Thing id="4">
        <Foo id="11" parentId="15"/>
        <Foo id="12" parentId="14"/>
        <Foo id="13" parentId="11"/>
        <Foo id="14" parentId="12"/>
        <Foo id="15"/>
    </Thing>
</Xml>

产生正确的结果,不包括任何无法到达的元素

<Xml>
   <Thing id="1">
      <Foo id="12">
         <Foo id="11" parentId="12"/>
      </Foo>
      <Thing id="2" parentId="1">
         <Thing id="3" parentId="2"/>
      </Thing>
   </Thing>
   <Thing id="4">
      <Foo id="15">
         <Foo id="11" parentId="15">
            <Foo id="13" parentId="11"/>
         </Foo>
      </Foo>
   </Thing>
</Xml>
于 2012-05-26T01:58:35.043 回答