Как я могу узнать родительский элемент при использовании iterparse метода cElementTree? - PullRequest
2 голосов
/ 17 февраля 2012

Я хочу зациклить элементы XML-файла и выдать каждый элемент, если только родитель не является функцией.

Так в псевдокоде

    for event, element in cElementTree.iterparse('../test.xml'):
        if parentOf_element != 'feature':
        yield element

Как я могу получить родительский элемент? Я знаю, что это возможно с помощью функции tree.getiterator (), но я не хочу создавать полное дерево, потому что файлы xml имеют размеры в несколько гигов.

Ответы [ 2 ]

2 голосов
/ 17 февраля 2012

Вы можете сделать это с помощью lxml .Он имеет getparent ().

В качестве альтернативы можно обрабатывать события start и end и пропускать feature потомков с cElementTree:

from xml.etree import cElementTree as etree

in_feature_tag = False
for event, element in etree.iterparse('test.xml', events=('start', 'end')):
    if element.tag == 'feauture':
        in_feature_tag = event == 'start'
    if event == 'end' and not in_feature_tag:
        yield element
1 голос
/ 18 февраля 2012

Если вы включите start события, вы можете отслеживать узлы-предки, используя стек.Если вы действительно хотите подавить всех потомков <feature>, а не только детей, вы можете использовать простой флаг, как показано в другом ответе.

Вы можете использовать root.clear(), чтобы уничтожить всеэлементы.Прочитайте это .

Код:

import xml.etree.cElementTree as et
# Produces identical answers with import lxml.etree as et
import cStringIO

def normtext(t):
    return repr("" if t is None else t.strip())

def dump(el):
    print el.tag, normtext(el.text), normtext(el.tail), el.attrib

def my_filtered_elements(source, skip_parent_tag="feature"):
    # get an iterable
    context = et.iterparse(source, events=("start", "end"))
    # turn it into an iterator
    context = iter(context)
    # get the root element
    event, root = context.next()
    tag_stack = [None, root.tag]
    for event, elem in context:
        # print event, elem.tag, tag_stack
        if event == "start":
            tag_stack.append(elem.tag)
        else:
            assert event == "end"
            my_tag = tag_stack.pop()
            assert my_tag == elem.tag
            parent_tag = tag_stack[-1]
            if parent_tag is not None and parent_tag != skip_parent_tag:
                dump(elem)
                # yield elem
            root.clear()

def other_filtered_elements(source, skip_parent_tag="feature"):            
    in_feature_tag = False
    for event, element in et.iterparse(source, events=('start', 'end')):
        if element.tag == skip_parent_tag:
            in_feature_tag = event == 'start'
        if event == 'end' and not in_feature_tag:
            dump(element)            

test_input = """
<top>
    <lev1 guff="1111">
        <lev2>aaaaa</lev2>
        <lev2>bbbbb</lev2>
    </lev1>
    <feature>
        feat text 1
        <fchild>fcfcfcfc
            <fgchild>ggggg</fgchild>    
        </fchild>
        feat text 2
    </feature>
    <lev1 guff="2222">
        <lev2>ccccc</lev2>c-tail
        <lev2>ddddd</lev2>d-tail
        <notext1></notext1>e-tail
        <notext2 />f-tail
     </lev1>g-tail
</top>
"""

print "=== me ==="
my_filtered_elements(cStringIO.StringIO(test_input))
print "=== other ==="
other_filtered_elements(cStringIO.StringIO(test_input))

Вывод ниже.По узлам lev1 вы заметите, что root.clear() не удаляет элементы, которые еще не были полностью проанализированы.Это означает, что объем используемой памяти составляет O (глубина дерева), а не O (общее количество элементов в дереве)

=== me ===
lev2 'aaaaa' '' {}
lev2 'bbbbb' '' {}
lev1 '' '' {'guff': '1111'}
fgchild 'ggggg' '' {}          <<<=== do you want this?
feature 'feat text 1' '' {}
lev2 'ccccc' 'c-tail' {}
lev2 'ddddd' 'd-tail' {}
notext1 '' 'e-tail' {}
notext2 '' 'f-tail' {}
lev1 '' 'g-tail' {'guff': '2222'}
=== other ===
lev2 'aaaaa' '' {}
lev2 'bbbbb' '' {}
lev1 '' '' {'guff': '1111'}
feature 'feat text 1' '' {}
lev2 'ccccc' 'c-tail' {}
lev2 'ddddd' 'd-tail' {}
notext1 '' 'e-tail' {}
notext2 '' 'f-tail' {}
lev1 '' 'g-tail' {'guff': '2222'}
top '' '' {}                           <<<=== do you want this?
...