2011-03-21 95 views
1

我有一个大的XML文档,看起来像这样:蟒蛇XML查询获取父

<Node name="foo"> 
    <Node name="16764764625"> 
     <Val name="type"><s>3</s></Val> 
     <Val name="owner"><s>1</s></Val> 
     <Val name="location"><s>4</s></Val> 
     <Val name="brb"><n/></Val> 
     <Val name="number"><f>24856</f></Val> 
     <Val name="number2"><f>97000.0</f></Val> 
    </Node> 
    <Node name="1764466544"> 
     <Val name="type"><s>1</s></Val> 
     <Val name="owner"><s>2</s></Val> 
     <Val name="location"><s>6</s></Val> 
     <Val name="brb"><n/></Val> 
     <Val name="number"><f>265456</f></Val> 
     <Val name="number2"><f>99000.0</f></Val> 
    </Node> 
    ... 
</Node> 

我的任务是获取父节点的值:通过执行1764466544(在第2节点名的值)搜索以查找节点Val name =“number”的子元素是否包含265456

我一直在做一堆关于XPath和ElementTree的阅读,但我仍然不确定从哪里开始实际查询此内容。寻找例子...我找不到任何引用父节点的结果。

仍然是新的python ..任何建议,将不胜感激。

由于

回答

3

此XPath:

/Node/Node[Val[@name='number']/f='265456']/@name 

输出:

1764466544 
+0

@itwb - 我从来没有在Python中尝试过XPath,所以这部分取决于你,但上面的XPath在抽象中起作用。在这里测试它,例如:http://www.xmlme.com/XpathTool.aspx – 2011-03-21 05:18:47

+0

是的,谢谢你。现在我收到了这个错误:SyntaxError:无法在元素上使用绝对路径。 – itwb 2011-03-21 05:30:24

+0

我在这里不熟悉的领域,但[此链接](http://nltk.googlecode.com/svn/trunk/doc/api/nltk.etree.ElementPath-pysrc.html)显示XPath表达式的以下代码用一个前导'/':'raise SyntaxError(“不能在元素上使用绝对路径”)。也许尝试相对表达?这个节点/节点[Val [@ name ='number']/f ='265456']/@ name'或这个节点/节点[Val [@ name ='number']/f ='265456' ]/@ name' – 2011-03-21 05:40:56

3

不幸的是,使用ElementTree的API时,每个Element对象没有返回参考到它的父母,所以你c从已知点开始注册树。相反,你必须找到可能的父对象并过滤你想要的对象。

这通常用XPath表达式来完成。但是,ElementTree仅支持XPath的一个子集(see the docs),其中最有用的部分仅添加到ElementTree 1.3中,该元素仅附带Python 2.7+或3.2+。

即使ElementTree的XPath不能使用你的文件 - 没有办法根据节点的文本,只有它的属性(或属性值)进行选择。

我的实验只找到了两种方法可以继续使用ElementTree。如果您使用Python 2.7+(或者能够下载并安装更新版本的ElementTree以使用较旧的Python版本),则可以使用修改XML文件的格式以将数字作为属性,例如

<Val name="number"><f val="265456" /></Val> 

那么下面的Python代码将退出感兴趣的节点:

import xml.etree.ElementTree as ETree 
tree = ETree.ElementTree(file='sample.xml') 
nodes = tree.findall(".//Node/Val[@name='number']/f[@val='265456']....") 

对于年龄较大的蟒蛇,或者如果您不能修改XML格式,则必须手动过滤无效节点。以下为我工作:

import xml.etree.ElementTree as ETree 
tree = ETree.ElementTree(file='sample.xml') 
all = tree.findall(".//Node") 
nodes = [] 

# Filter matching nodes and put them in the nodes variable. 
for node in all: 
    for val in node.getchildren(): 
     if val.attrib['name'] == 'number' and val.getchildren()[0].text =='265456': 
      nodes.append(node) 

这些解决方案也不是我所说的理想的,但他们是唯一的我已经能够使与ElementTree库工作(因为那是你上述使用)。使用第三方库可能会更好,而不是使用内置的库;有关选项列表,请参见the Python wiki entry on XMLlxml是广泛使用的libxml2库的Python绑定,并且会是我首先建议查看的那个。它具有XPath支持,因此您应该能够使用来自其他答案的查询。

+0

真的很烦人,Python添加了一些XPath支持,但我不能使用“..”语法从当前节点上去。应该在Python [文档](https://docs.python.org/2.7/library/xml.etree.elementtree.html)中说明。其实[文档](https://docs.python.org/2.7/library/xml.etree.elementtree.html)声明支持该语法。只要你不超过当前元素,也许它是受支持的,例如“人/..”?我花了大约一个小时试图弄清楚为什么这不起作用。 – Samuel 2014-11-26 22:10:04

0

以下功能帮助我解决了类似的情况。正如文档字符串解释的那样,它在一般情况下不起作用,但是如果节点是唯一的,它应该有所帮助。

def get_element_ancestry(root, element): 
'''Return a list of ancestor Elements for the given element. 

If both root and element are of type xml.etree.ElementTree.Element, and if 
the given root contains the given element as a descendent, then return a 
list of direct xml.etree.ElementTree.Element ancestors, starting with root 
and ending with element. Otherwise, return an empty list. 

The xml.etree.ElementTree module offers no function to return the parent of 
a given Element, presumably because an Element may be in more than one tree, 
or even multiple times within a given tree, so its parent depends on the 
context. This function provides a solution in the specific cases where the 
caller either knows that the given element appears just once within the 
tree or is satisfied with the first branch to reference the given element. 
''' 
result = [] 
xet = xml.etree.ElementTree 
if not xet.iselement(root) or not xet.iselement(element): 
    return result 
xpath = './/' + element.tag \ 
    + ''.join(["[@%s='%s']" % a for a in element.items()]) 
parent = root 
while parent != None: 
    result.append(parent) 
    for child in parent.findall('*'): 
     if child == element: 
      result.append(element) 
      return result 
     if child.findall(xpath).count(element): 
      parent = child 
      break 
    else: 
     return [] 
return result 
+0

OP已经超过3年了...这是个好主意,可以澄清您的答案现在是否适用于现有版本,用于旧版本之前,旧版本,还是您认为相关知识的说明。 – gmo 2014-07-08 22:24:35