给定此 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 值的"事物"节点都应嵌套在相应的 Thing 节点下。每个具有 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>
我看不出如何获取当前节点@id值以在基于 parentId 值的 Xpath 匹配兄弟姐妹的谓词中使用。
然后我想将当前节点的兄弟姐妹嵌套在它下面,其中兄弟姐妹@ParentId等于我的@id。
首先,请注意提供的 XML 文档中的一个错误:
<Foo id="12" parentId="14"/>
<Foo id="13" parentId="11"/>
<Foo id="14" parentId="12"/>
Foo
与id
12 之间存在循环关系,Foo
与 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>