Как рекурсивно перебрать файл xml, получить доступ к дочерним узлам / элементам и сохранить их данные, используя Python? - PullRequest
0 голосов
/ 19 апреля 2020

У меня есть файл XML, как показано ниже. Теперь мне нужно получить доступ к port->name, port->wire->direction, port->wire->driver->defval. Файл XML очень большой по размеру.

Как мне подойти к этому?

<spirit:Bus> 
    <spirit:Ports>   
        <spirit:port>
            <spirit:name>ABCPORT</spirit:name>
            <spirit:description>SOME DESCRIPTION</spirit:description>
            <spirit:wire>
                <spirit:direction>INPUT</spirit:direction>
                <spirit:driver>
                    <spirit:defaultValue>0</spirit:defaultValue>
                </spirit:driver>
            </spirit:wire>
        </spirit:port>
        <spirit:port>
            <spirit:name>PQRPORT</spirit:name>
            <spirit:description>SOME DESCRIPTION</spirit:description>
            <spirit:wire>
                <spirit:direction>OUTPUT</spirit:direction>
            </spirit:wire>
        </spirit:port>        
    </spirit:ports>
</spirit:Bus>

Ответы [ 2 ]

0 голосов
/ 21 апреля 2020

Чтобы правильно отформатировать XML, я добавил пространство имен к вашему образцу:

<spirit:Bus xmlns:spirit="http://dummy.com">
    ...
</spirit:Bus>

, но Шина по-прежнему является root узлом, как в вашем примере. Конечно, вы можете изменить указанный URL-адрес на любой, какой у вас будет sh.

. Для выполнения вашей задачи исключительно в ElementTree вы можете использовать следующий код:

import xml.etree.ElementTree as et

tree = et.parse('Input.xml')
root = tree.getroot()
ns = {'spirit': 'http://dummy.com'}
for nd in root.findall('spirit:Ports/spirit:port', ns):
    print(nd.tag.split('}')[1], nd.findtext('spirit:name', namespaces=ns),
        nd.findtext('spirit:wire/spirit:direction', namespaces=ns),
        nd.findtext('spirit:wire/spirit:driver/spirit:defaultValue', namespaces=ns))

Обратите внимание, что ваш XML содержит спецификацию пространства имен, поэтому вы также должны указать ее в коде.

Мой код также показывает, как получить local имя узла ( без пространства имен).

Результат для вашего образца:

port ABCPORT INPUT 0
port PQRPORT OUTPUT None
0 голосов
/ 20 апреля 2020

Я считаю, что лучший способ решить эту проблему с помощью l xml и xpath:

from lxml import etree

#the xml below is somewhat different than the one in the question, because of a type and the declare namespaces

spirit = """<?xml version="1.0" encoding="UTF-8"?>
<doc xmlns:spirit="http://example.com">
   <spirit:Bus>
      <spirit:Ports>
         <spirit:port>
            <spirit:name>ABCPORT</spirit:name>
            <spirit:description>SOME DESCRIPTION</spirit:description>
            <spirit:wire>
               <spirit:direction>INPUT</spirit:direction>
               <spirit:driver>
                  <spirit:defaultValue>0</spirit:defaultValue>
               </spirit:driver>
            </spirit:wire>
         </spirit:port>
         <spirit:port>
            <spirit:name>PQRPORT</spirit:name>
            <spirit:description>SOME DESCRIPTION</spirit:description>
            <spirit:wire>
               <spirit:direction>OUTPUT</spirit:direction>
            </spirit:wire>
         </spirit:port>
      </spirit:Ports>
   </spirit:Bus>
</doc>
"""

doc = etree.XML(spirit.encode('utf-8'))
ports = doc.xpath('//*[local-name()="port"]')
for port in ports:
    try:
        print("Port-",port.xpath('.//*[local-name()="name"]')[0].text)
        print("Direction",port.xpath('.//*[local-name()="direction"]')[0].text)
        print("Default value",port.xpath('.//*[local-name()="defaultValue"]')[0].text)
    except:
        continue

Вывод:

Port- ABCPORT
Direction INPUT
Default value 0
Port- PQRPORT
Direction OUTPUT
...