анализ xml без рекурсивного поискового питона - PullRequest
3 голосов
/ 05 февраля 2011

Это сводит меня с ума, и я, вероятно, надолго взломал это, поэтому был бы признателен за некоторую помощь, чтобы предотвратить потерю / восстановление моего здравомыслия!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 с Python и минидомом .Может кто-нибудь помочь с тем, как я мог бы пойти по этому поводу?Я могу опубликовать свой код, но он так взломан вместе / сломан, я не думаю, что он будет полезен ни человеку, ни животному!

Приветствия

Дейв

Ответы [ 4 ]

4 голосов
/ 05 февраля 2011

Используйте lxml .В частности, XPath.Вы можете получить все элементы layer, независимо от уровня, через "//layer" и layer с идентификатором id до "//layer[id='{}'][0]".format(id).keyword элементов непосредственно под элементом (или несколькими элементами) на ".../keyword" (где ... - это запрос, который выдает узлы, чьи потомки должны быть найдены).

Получение глубины данного узлане так тривиально, но все же легко.Я не нашел существующей функции (afaik, это вне области XPath - хотя вы можете проверять глубину в запросе, вы только возвращаете элементы, т.е. вы можете возвращать узлы с определенной глубиной, но не самой глубиной)Итак, вот что было сделано вручную (без рекурсии, поскольку в этом нет необходимости - но в целом работа с XML означает работу с рекурсией, нравится вам это или нет!):

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

Возможно нечто очень похожеес DOM, если вы достаточно глупы, чтобы не использовать лучшую из существующих библиотек Python XML;)

2 голосов
/ 05 февраля 2011

Вот решение с 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']
1 голос
/ 05 февраля 2011

Вы можете либо рекурсивно пройтись по 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))
0 голосов
/ 05 февраля 2011

Попробуйте перебрать все дочерние узлы в рекурсивной функции, проверяя каждый на наличие имени тега.то есть

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

В качестве альтернативы попробуйте использовать другую библиотеку XML, например Amara или lxml , которая имеет XPath .С XPath вы можете иметь гораздо больше возможностей для поиска в дереве DOM с очень небольшим количеством кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...