不使用递归搜索python的xml解析

2024-06-17 11:28:52 发布

您现在位置:Python中文网/ 问答频道 /正文

这让我精神错乱,我可能已经花了很长时间了,所以希望能帮我防止失去理智/恢复理智!基于食物的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]

这是一个示例文件-

^{pr2}$

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

如果我做一个

xmldoc = minidom.parse(self.filename)

layers = xmldoc.getElementsByTagName('layer')

layers只返回所有的layer元素,这是非常多的,并且在我理解的范围内没有深度/层次结构的概念,因为它进行递归搜索。在

以下是我不需要提供的好概念。XML Parsing with Python and minidom。有谁能帮我解决这个问题吗?我可以发布我的代码,但它是如此的骇人听闻/基本损坏,我认为它不会用于人或野兽!在

干杯

戴夫


Tags: 文件layer概念精神layers图表rootxml
3条回答

下面是一个使用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]

输出

^{pr2}$

使用lxml。特别是XPath。您可以通过"//layer"和id为id"//layer[id='{}'][0]".format(id)获得所有layer元素,而不考虑级别。由".../keyword"直接位于一个元素(或多个元素)下的keyword元素(其中...是一个查询,它生成了应该搜索其后代的节点)。在

获取给定节点的深度不是那么简单,但是仍然很容易。我没有找到一个现有的函数(afaik,这超出了XPath的范围-虽然可以检查查询中的深度,但只返回元素,也就是说,可以返回具有特定深度的节点,但不能返回深度本身),所以这里有一个手动生成的函数(不需要递归,因为这不是必需的-但一般来说,使用XML意味着使用递归,不管你喜欢与否公司名称:

def depth(node):
    depth = 0
    while node.getparent() is not None:
        node = node.getParent()
        depth += 1
    return depth

如果您愚蠢到不使用现有最好的pythonxml库,那么DOM也可能出现类似的情况;)

您可以递归地遍历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))

相关问题 更多 >