Модуль ElementTree для разделения содержимого xml

Я пытаюсь разобрать файл xml и расположить его в таблице, разделив содержимое как isElement, isAttribute, Value, Text.

Как мне использовать модуль ElementTree для достижения этой цели? Я знаю, что это возможно с помощью модуля minidom.

Причина, по которой я хочу использовать ElementTree, связана с эффективностью. Пример того, чего я пытаюсь достичь, доступен здесь: http://python.zirael.org/e-gtk-treeview4.html

Любые советы о том, как разделить содержимое xml на элемент, подэлемент и т. д. с помощью модуля ElementTree?

Это то, что у меня есть до сих пор:

import xml.etree.cElementTree as ET

filetree = ET.ElementTree(file = "some_file.xml")
for child in filetree.iter():
     print child.tag, child.text, child.attrib

Для следующего примера XML-файла:

    <?xml version="1.0"?>
    <data>
        <country name="Liechtenstein">
            <rank>1</rank>
            <year>2008</year>
            <gdppc>141100</gdppc>
            <neighbor name="Austria" direction="E"/>
            <neighbor name="Switzerland" direction="W"/>
        </country>
        <country name="Singapore">
            <rank>4</rank>
            <year>2011</year>
            <gdppc>59900</gdppc>
            <neighbor name="Malaysia" direction="N"/>
        </country>
        <country name="Panama">
            <rank>68</rank>
            <year>2011</year>
            <gdppc>13600</gdppc>
            <neighbor name="Costa Rica" direction="W"/>
            <neighbor name="Colombia" direction="E"/>
        </country>
    </data>

Я получаю это как вывод:

    data 
         {}
    country 
             {'name': 'Liechtenstein'}
    rank 1 {}
    year 2008 {}
    gdppc 141100 {}
    neighbor None {'direction': 'E', 'name': 'Austria'}
    neighbor None {'direction': 'W', 'name': 'Switzerland'}
    country 
             {'name': 'Singapore'}
    rank 4 {}
    year 2011 {}
    gdppc 59900 {}
    neighbor None {'direction': 'N', 'name': 'Malaysia'}
    country 
             {'name': 'Panama'}
    rank 68 {}
    year 2011 {}
    gdppc 13600 {}
    neighbor None {'direction': 'W', 'name': 'Costa Rica'}
    neighbor None {'direction': 'E', 'name': 'Colombia'}

Я нашел что-то подобное в другом посте, но он использует модуль DOM. Пройтись по всем узлам XML в структуре с вложенными элементами< /а>

Основываясь на полученном комментарии, я хочу добиться этого:

    data (type Element)
         country(Element)
              Text = None
              name(Attribute)
                 value: Liechtenstein
              rank(Element)
                  Text = 1
              year(Element)
                  Text = 2008
              gdppc(Element)
                  Text = 141100
              neighbour(Element)
                  name(Attribute)
                      value: Austria
                  direction(Attribute)
                      value: E
              neighbour(Element)
                  name(Attribute)
                      value: Switzerland
                  direction(Attribute)
                      value: W

         country(Element)
              Text = None
              name(Attribute)
                 value: Singapore
              rank(Element)
                  Text = 4

Я хочу иметь возможность представлять свои данные в виде дерева, как указано выше. Для этого мне нужно следить за их отношениями. Надеюсь, это проясняет вопрос.


person Saed    schedule 02.09.2015    source источник
comment
Пожалуйста, смотрите отредактированный пост для кода.   -  person Saed    schedule 02.09.2015
comment
Как и в примере кода Gtk, вам нужно будет написать рекурсивную функцию/метод, который добавляет каждый узел в XML-документе в TreeStore. Есть разница в том, как ElementTree обрабатывает текст: это не особый тип узла, но каждый элемент имеет атрибуты text и tail.   -  person BlackJack    schedule 02.09.2015
comment
Что не так с кодом, который вы разместили? Что вы намеревались сделать, что это не так?   -  person barny    schedule 02.09.2015
comment
@barny В коде, который я разместил, нет способа отследить, например: если элемент является подэлементом другого, предыдущего и т. д. В основном иерархия не ясна, используя приведенный выше код.   -  person Saed    schedule 03.09.2015
comment
Я немного сбит с толку, потому что в вашем вопросе говорилось, что вы хотите сгладить xml, в частности, нет упоминания о дочернем/родительском. Можете ли вы более четко описать, чего вы хотите достичь?   -  person barny    schedule 03.09.2015
comment
@barny Пожалуйста, смотрите измененный вопрос. Я добавил окончательный результат, который мне нужен. Спасибо. Я думаю, как сказал БлэкДжек, мне нужна рекурсивная функция. Любая идея, есть ли у вас встроенная функция в ElementTree, чтобы информировать пользователя о количестве или атрибутах, доступных элементах и ​​т. д.?   -  person Saed    schedule 03.09.2015
comment
Вы пробовали искать? stackoverflow.com/questions/17310681/ и посмотрите под заголовком "Наблюдение за событиями во время синтаксического анализа" здесь pymotw.com/2/xml/etree/ElementTree/parse.html Я искал: вложенная печать python elementtree xml   -  person barny    schedule 03.09.2015
comment
Как насчет чтения документации модуля ElementTree? Element объекты представляют собой последовательности, содержащие их непосредственные дочерние элементы, атрибуты XML хранятся в словаре, сопоставляющем имена атрибутов со значениями. Словарь — это атрибут с именем attrib для Element объектов. И последовательности, и словари поддерживают функцию len() для определения количества элементов.   -  person BlackJack    schedule 03.09.2015
comment
@BlackJack Спасибо .. посмотрю на это.   -  person Saed    schedule 03.09.2015
comment
@барни Спасибо. Я прочитаю эти документы и посмотрю, как далеко я продвинусь.   -  person Saed    schedule 03.09.2015
comment
одна вещь, которая может вам помочь: встроенный модуль ElementTree не имеет концепции родителей, но если вы используете модуль lxml (доступный на pypi), он очень похож на ElementTree (даже по большей части совместим с API), за исключением того, что lxml Элементы знают, кто их родитель, и вы можете подняться по дереву откуда угодно.   -  person Corley Brigman    schedule 03.09.2015


Ответы (2)


Element объекты представляют собой последовательности, содержащие их непосредственные дочерние элементы. XML-атрибуты хранятся в словаре, где имена атрибутов сопоставляются со значениями. Здесь нет текстовых узлов, как в DOM. Текст хранится как атрибуты text и tail. Текст внутри элемента, но перед первым подэлементом сохраняется в text, а текст между этим элементом и следующим сохраняется в tail. Итак, если мы возьмем пример gtk-treeview4-2.py из Деревовид IV. - отображение деревьев мы должны переписать этот DOM-код:

# ...
import xml.dom.minidom as dom
# ...

    def create_interior(self):
        # ...
        doc = dom.parse(self.filename)
        self.add_element_to_treestore(doc.childNodes[0], None)
        # ...

    def add_element_to_treestore(self, e, parent):
        if isinstance(e, dom.Element):
            me = self.model.append(parent, [e.nodeName, 'ELEMENT', ''])
            for i in range(e.attributes.length):
                a = e.attributes.item(i)
                self.model.append(me, ['@' + a.name, 'ATTRIBUTE', a.value])
            for ch in e.childNodes:
                self.add_element_to_treestore(ch, me)
        elif isinstance(e, dom.Text):
            self.model.append(
                parent, ['text()', 'TEXT_NODE', e.nodeValue.strip()])

следующим образом, используя ElementTree:

# ...
from xml.etree import ElementTree as etree
# ...

    def create_interior(self):
        # ...
        doc = etree.parse(self.filename)
        self.add_element_to_treestore(doc.getroot())
        # ...

    def add_element_to_treestore(self, element, parent=None):
        path = self.model.append(parent, [element.tag, 'ELEMENT', ''])
        for name, value in sorted(element.attrib.iteritems()):
            self.model.append(path, ['@' + name, 'ATTRIBUTE', value])
        if element.text:
            self.model.append(
                path, ['text()', 'TEXT_NODE', element.text.strip()]
            )
        for child in element:
            self.add_element_to_treestore(child, path)
            if element.tail:
                self.model.append(
                    path, ['text()', 'TEXT_NODE', element.tail.strip()]
                )

Снимок экрана с данными вашего примера и полностью развернутым первым поддеревом:

Скриншот с примерными данными


Обновление: добавлен снимок экрана с примерами данных и соответствующими строками импорта в коде.

person BlackJack    schedule 05.09.2015
comment
Спасибо за это. Это своего рода то, что я был после. Я принимаю это как ответ. - person Saed; 08.09.2015

Возможно, это не совсем то, что вам нужно, но вы можете преобразовать XML с помощью XSLT, чтобы получить древовидную структуру:

XSLT (включая табуляции и разрывы строк)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8"/>

<xsl:template match="data">

<xsl:variable name="tabonce"><xsl:text>&#10;&#x9;</xsl:text></xsl:variable>
<xsl:variable name="tabtwice"><xsl:text>&#10;&#x9;&#x9;</xsl:text></xsl:variable>

<data>
    data (type Element)<xsl:text>&#10;&#x9;</xsl:text>
    <xsl:for-each select="country">
           <xsl:value-of select="concat(local-name(.), '(Element)')"/>
           Text = <xsl:value-of select="concat('None', $tabonce)"/> 
           <xsl:value-of select="concat(name(@*), '(Attribute)')"/>
              value: <xsl:value-of select="concat(@*, $tabonce)"/>          

        <xsl:for-each select="*">
        <xsl:value-of select="concat(local-name(.), '(Element)')"/>     
              Text = <xsl:value-of select="concat(., $tabonce)"/> 

              <xsl:if test="@*">
                 <xsl:text>&#x9;</xsl:text><xsl:value-of select="concat(name(@name), '(Attribute)')"/>
                 value: <xsl:value-of select="concat(@name, $tabtwice)"/>  
                 <xsl:value-of select="concat(name(@direction), '(Attribute)')"/>
                 value: <xsl:value-of select="concat(@direction, $tabonce)"/> 
              </xsl:if>

        </xsl:for-each>
        <xsl:text>&#10;&#x9;</xsl:text>

    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
</data>    

</xsl:template>
</xsl:stylesheet>

Скрипт Python с использованием модуля lxml:

import lxml.etree as ET

dom = ET.parse('C:\Path\To\XMLfile.xml')
xslt = ET.parse('C:\Path\To\XSLfile.xsl')
transform = ET.XSLT(xslt)
newdom = transform(dom)

tree_out = ET.tostring(newdom, encoding='UTF-8', pretty_print=True,  xml_declaration=True)
print(tree_out)

xmlfile = open('C:\Path\To\OutputPath.xml','wb')
xmlfile.write(tree_out)
xmlfile.close()

XML-вывод

<?xml version='1.0' encoding='UTF-8'?>
<data>
    data (type Element)
    country(Element)
        Text = None
    name(Attribute)
        value: Liechtenstein
    rank(Element)       
        Text = 1
    year(Element)       
        Text = 2008
    gdppc(Element)      
        Text = 141100
    neighbor(Element)       
        Text = 
        name(Attribute)
            value: Austria
        direction(Attribute)
            value: E
    neighbor(Element)       
        Text = 
        name(Attribute)
            value: Switzerland
        direction(Attribute)
            value: W

    country(Element)
        Text = None
    name(Attribute)
        value: Singapore
    rank(Element)       
        Text = 4
    year(Element)       
        Text = 2011
    gdppc(Element)      
        Text = 59900
    neighbor(Element)       
        Text = 
        name(Attribute)
            value: Malaysia
        direction(Attribute)
            value: N

    country(Element)
        Text = None
    name(Attribute)
        value: Panama
    rank(Element)       
        Text = 68
    year(Element)       
        Text = 2011
    gdppc(Element)      
        Text = 13600
    neighbor(Element)       
        Text = 
        name(Attribute)
            value: Costa Rica
        direction(Attribute)
            value: W
    neighbor(Element)       
        Text = 
        name(Attribute)
            value: Colombia
        direction(Attribute)
            value: E


</data>
person Parfait    schedule 07.09.2015
comment
Спасибо за этот ответ. Я не знал об этом подходе, теперь я знаю :) Тем не менее, я принял ответ ниже, так как он является более подходящим ответом на исходный вопрос. - person Saed; 08.09.2015