的xml:
<root>
<a auto='1'>
<b>
<c auto="1">
<d auto="1"></d>
</c>
</b>
<e auto="1">
<f>
<g auto="1"></g>
</f>
</e>
</a>
</root>
工作是:
找到所有元素:
1,是上下文元素的后代
2,具有'自动'属性
3,最高级别(自我和上下文元素之间没有祖先自动属性)
因此,如果上下文节点 a ,则应返回 c 和 e 。
我在我的php课程中实现它:
$tempId='XDFAY69LA';
$this->setAttribute('tempId',$tempId);
$path=".//*[@auto and not(ancestor::*[@auto and ancestor::*[@tempId='$tempId']])]";
$ar=$this->getElementsByXPath($path);
$this->removeAttribute('tempId');
但是我发现查询很慢,也许是..,因为查询过于复杂?,有没有办法做得更好?
我正在写一个测试,请看看:
<?php $xml=' <root> <a auto="1" tempId="current"> <b> <c auto="1"> <d auto="1"></d> </c> </b> <e auto="1"> <f> <g auto="1"></g> </f> </e> </a> </root> '; $doc=new DomDocument(); $tempId='XDFAY69LA'; $doc->loadXml($xml); $domxpath=new DOMXPath($doc); $a=$domxpath->query('a')->item(0); $path=".//*[@auto and not(ancestor::*[@auto and ancestor::*[@tempId='$tempId']])]"; $start=microtime(true); for($n=0;$n<1000;$n++){ //run 1000 times $a->setAttribute('tempId',$tempId); $ar=$domxpath->query($path,$a); $a->removeAttribute('tempId'); for($i=0;$i<$ar->length;$i++){ $node=$ar->item($i); //echo $node->tagName . "\n"; } } $cost=round(1000 * (microtime(true)-$start)); echo "time cost: $cost"; ?>
答案 0 :(得分:2)
使用强>:
.//*[@auto and $tempId = ancestor::*[@auto][1]/@tempId]
这将选择具有auto
属性的所有后代元素(上下文节点),其第一个具有auto
属性的祖先也具有tempId
属性,其值与该属性相同上下文节点的tempId
属性(后者存储在$tempId
变量中)。
这里我们假设没有两个不同的元素具有相同的tempId
属性值。
基于XSLT的快速验证:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="a">
<xsl:variable name="tempId" select="@tempId"/>
<xsl:copy-of select=
".//*[@auto and $tempId = ancestor::*[@auto][1]/@tempId]"/>
</xsl:template>
</xsl:stylesheet>
在提供的XML文档上应用此转换时:
<root>
<a auto="1" tempId="current">
<b>
<c auto="1">
<d auto="1"></d>
</c>
</b>
<e auto="1">
<f>
<g auto="1"></g>
</f>
</e>
</a>
</root>
生成了想要的正确结果(生成两个元素c
和e
):
<c auto="1">
<d auto="1"/>
</c>
<e auto="1">
<f>
<g auto="1"/>
</f>
</e>
仅在XPath表达式中无法改善性能,效率低下是由于必须使用//
XPath伪运算符。
如果使用XSLT,可以使用密钥获得有效的解决方案:
<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="kfirstDescendents" match="*[@auto]"
use="generate-id(ancestor::*[@auto][1])"/>
<xsl:template match="a">
<xsl:copy-of select=
"key('kfirstDescendents', generate-id())"/>
</xsl:template>
</xsl:stylesheet>
此转换产生与第一个相同的结果,并且在具有许多具有auto
属性的嵌套元素的文档上显着更快。
如果绝对排除使用XSLT,可以使用哈希表实现与XSLT密钥相同的效果(抱歉,不知道PHP)。
答案 1 :(得分:1)
从XPath开始:
.//*[@auto and not(ancestor::*[@auto and ancestor::*[@tempId='$tempId']])]
怎么样:
.//*[@auto and not(ancestor::*[@auto][ancestor::*[@tempId='$tempId']])]
甚至,
.//*[@auto and count(ancestor::*[@auto][ancestor::*[@tempId='$tempId']])=0]
答案 2 :(得分:0)
我的想法是简化它。
$path=".//*[@auto and not(ancestor::*[@auto and not(@tempId='$tempId'))]";
“祖先:: * [@ tempId = '$ tempId']”
到
“不(@tempId = '$ tempId')”
//编辑内容:淘汰详细