我正在搜索一种可重复使用的方法,以从xml文档中获取所有节点,这些节点是从给定节点的后代引用的(通过id),但它们本身不是给定节点的后裔。例如:
<root>
<somenode>
<a id="a1"/>
<aref ref="a1"/>
</somenode>
<somenode>
<a id="a2"/>
<aref ref="a1"/>
<aref ref="a2"/>
</somenode>
</root>
如果给定的节点是/root/somenode[1],则生成的节点集应该为空。每个引用的a都是/root/somenode[1]的子级。另一方面,如果给定的节点是/root/somenode[2],则生成的节点集应该包含/root/ssomenode[1]/a[1],而不包含其他内容。
重要的是,生成的节点集始终以相同的方式进行排序。此外,解决方案应该只使用xslt-1.0和xsltproc中内置的exlst扩展(库的确切版本:"使用libxml 20708、libxslt 10126和libexslt 815")
提前感谢,Jost
根据http://www.exslt.org/set/functions/difference/index.html,libxslt支持该函数,因此执行
<xsl:key name="el-by-id" match="*" use="id"/>
和
<xsl:variable name="refs" select="set:difference(key('el-by-id', descendant::*/@ref), descendant::*)"/>
应该在模板中执行(使用xmlns:set="http://exslt.org/sets"
),将您的节点作为上下文节点。
此处提供的解决方案不使用任何扩展函数,并且在任何XSLT处理器上都是100%可移植的。
使用xslt函数generate-id()
:
//*[@id = /*/somenode[1]/aref/@ref
and
not(generate-id(ancestor::somenode) = generate-id(/*/somenode[1]))
]
使用钥匙可以提高效率,
这也可以使用节点集交集的Kayessian公式表示为单个XPath 1.0表达式:
$ns1[count(.|$ns2) = count($ns2)]
在纯XPath 2.0中,将使用is
运算符而不是generate-id()
这是一个完整的演示:
<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:template match="/">
"<xsl:copy-of select=
"//*[@id = /*/somenode[1]/aref/@ref
and
not(generate-id(ancestor::somenode) = generate-id(/*/somenode[1]))
]"/>"
============
"<xsl:copy-of select=
"//*[@id = /*/somenode[2]/aref/@ref
and
not(generate-id(ancestor::somenode) = generate-id(/*/somenode[2]))
]"/>"
</xsl:template>
</xsl:stylesheet>
当此转换应用于所提供的XML文档时:
<root>
<somenode>
<a id="a1"/>
<aref ref="a1"/>
</somenode>
<somenode>
<a id="a2"/>
<aref ref="a1"/>
<aref ref="a2"/>
</somenode>
</root>
计算XPath表达式,并将所选节点复制到输出:
""
============
"<a id="a1" />"