没有递归搜索python的xml解析

时间:2011-02-05 18:28:15

标签: python xml dom

这让我精神恍惚,而且我可能已经长时间不知所措了,所以我会感谢一些帮助以防止失去/恢复我的理智!基于食物的xml只是我希望实现的一个例子。

我有以下文件,我试图将其放入图表中,因此小麦和水果是深度为0的父母。印度人是小麦的孩子,深度为1,依此类推。

每个图层都有一些关键字。所以我想要的是

layer, depth, parent, keywords
wheat, 1, ROOT, [bread, pita, narn, loaf]  
indian, 2, wheat [chapati]
mumbai, 3, indian, puri 
fruit, 1,ROOT, [apple, orange, pear, lemon]

这是一个示例文件 -

<keywords>
    <layer id="wheat">
        <layer id="indian">
            <keyword>chapati</keyword>
            <layer id="mumbai">
                <keyword>puri</keyword>
            </layer>
        </layer>
        <keyword>bread</keyword>
        <keyword>pita</keyword>
        <keyword>narn</keyword>
        <keyword>loaf</keyword>
    </layer>
    <layer id="fruit">
        <keyword>apple</keyword>
        <keyword>orange</keyword>
        <keyword>pear</keyword>
        <keyword>lemon</keyword>
    </layer>

</keywords>

所以这不是一个图形问题,我可以做到这一点很容易。我正在努力解决XML问题。

如果我做了

xmldoc = minidom.parse(self.filename)

layers = xmldoc.getElementsByTagName('layer')

图层只返回所有图层元素,这个图层元素很多,并且根据我的理解,它没有深度/层次结构的概念,因为它会进行递归搜索。

以下帖子很好,但没有提供我需要的概念。 XML Parsing with Python and minidom。任何人都可以帮我解决这个问题吗?我可以发布我的代码,但是它一起被黑客攻击/有趣地破坏了我不认为它对人类和野兽都有用!

干杯

戴夫

4 个答案:

答案 0 :(得分:4)

使用lxml。特别是XPath。您可以通过layer和标识为"//layer"layer的{​​{1}}获取所有id个元素,无论其级别如何。由"//layer[id='{}'][0]".format(id)直接位于元素(或多个元素)下的keyword元素(其中".../keyword"是一个查询,用于生成应搜索其后代的节点)。

获取给定节点的深度并不是那么简单,但仍然很容易。我没有找到现有的函数(afaik,这是在XPath的域之外 - 你可以检查查询中的深度,你只返回元素,即你可以返回具有特定深度而不是深度本身的节点) ,所以这里是一个手动滚动的(没有递归,因为它没有必要 - 但一般来说,使用XML意味着使用递归,无论喜欢与否!):

...

如果你不愚蠢地不使用最好的Python XML库,那么DOM可能会有类似的东西;)

答案 1 :(得分:2)

以下是ElementTree的解决方案:

from xml.etree import ElementTree as ET
from io import StringIO
from collections import defaultdict

data = '''\
<keywords>
    <layer id="wheat">
        <layer id="indian">
            <keyword>chapati</keyword>
            <layer id="mumbai">
                <keyword>puri</keyword>
            </layer>
        </layer>
        <keyword>bread</keyword>
        <keyword>pita</keyword>
        <keyword>narn</keyword>
        <keyword>loaf</keyword>
    </layer>
    <layer id="fruit">
        <keyword>apple</keyword>
        <keyword>orange</keyword>
        <keyword>pear</keyword>
        <keyword>lemon</keyword>
    </layer>
</keywords>
'''

path = ['ROOT']  # stack for layer names
items = defaultdict(list)  # key=layer, value=list of items @ layer

f = StringIO(data)
for evt,e in ET.iterparse(f,('start','end')):
    if evt == 'start':
        if e.tag == 'layer':
            path.append(e.attrib['id']) # new layer added to path
        elif e.tag == 'keyword':
            items[path[-1]].append(e.text) # add item to last layer in path
    elif evt == 'end':
        if e.tag == 'layer':
            layer = path.pop()
            parent = path[-1]
            print layer,len(path),parent,items[layer]

输出

mumbai 3 indian ['puri']
indian 2 wheat ['chapati']
wheat 1 ROOT ['bread', 'pita', 'narn', 'loaf']
fruit 1 ROOT ['apple', 'orange', 'pear', 'lemon']

答案 2 :(得分:1)

您可以递归地遍历DOM treje(请参阅kelloti的答案)或从找到的节点确定信息:

xmldoc = minidom.parse(filename)
layers = xmldoc.getElementsByTagName("layer")

def _getText(node):
    rc = []
    for n in node.childNodes:
        if n.nodeType == n.TEXT_NODE:
            rc.append(n.data)
    return ''.join(rc)

def _depth(n):
    res = -1
    while isinstance(n, minidom.Element):
        n = n.parentNode
        res += 1
    return res

for l in layers:
    keywords = [_getText(k) for k in l.childNodes
                if k.nodeType == k.ELEMENT_NODE and k.tagName == 'keyword']
    print("%s %s %s" % (l.getAttribute("id"), _depth(l), keywords))

答案 3 :(得分:0)

尝试遍历递归函数中的所有子节点,检查每个子节点的标记名称。即。

def findLayer(node):
    for n in node.childNodes:
        if n.localName == 'layer':
            findLayer(n)
            # do things here

或者,尝试使用具有Amara功能的lxmlXPath等其他XML库。使用XPath,您可以使用非常少的代码更好地控制搜索DOM树。