Разбор XML с пространством имен в словарь - PullRequest
0 голосов
/ 20 июня 2019

Мне трудно следовать документации xml.etree.ElementTree относительно анализа XML-документа с пространством имен и вложенными тегами.

Для начала, XML-дерево, которое я пытаюсь разобрать, выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ROOT-MAIN xmlns="http://fakeurl.com/page">
    <Alarm> <--- I dont care about these types of objects
        <Node>
            <location>Texas></location>
            <name>John</name>
        </Node>
    </Alarm>
    <Alarm> <--- I care about these types of objects
        <CreateTime>01/01/2011</CreateTime>
        <Story>
            <Node>
                <Name>Ethan</name
                <Address category="residential>
                    <address>1421 Morning SE</address>
                </address>
            </Node>
        </Story>
        <Build>
            <Action category="build_value_1">Build was successful</Action>
        </Build>
        <OtherData type="string" meaning="favoriteTVShow">Purple</OtherData>
        <OtherData type="string" meaning="favoriteColor">Seinfeld</OtherData>
    </Alarm>
</ROOT-MAIN>

Я пытаюсь создать массив словарей, структура которых аналогична второй <Тревога> объект.При синтаксическом анализе этого XML-файла я делаю следующее:

import xml.etree.ElementTree as ET
tree = ET.parse('data/'+filename)
root = tree.getroot()


namespace= '{http://fakeurl.com/page}'

for alarm in tree.findall(namespace+'Alarm'):
    for elem in alarm.iter():
        try:
            creation_time = elem.find(namespace+'CreateTime')
            for story in elem.findall(namespace+'Story'):
                for node in story.findall(namespace+'Node'):
                    for Address in node.findall(namespace+'Address'):
                        address = Address.find(namespace+'address').text

            for build in elem.findall(namespace+'Build'):
                category= build.find(namespace+'Action').attrib
                action = build.find(namespace+'Action').text

            for otherdata in elem.findall(namespace+'OtherData'):
                #not sure how to get the 'meaning' attribute value as well as the text value for these <OtherData> tags  
        except:
            pass

Верно, я просто пытаюсь получить значения для:

  1. <адрес>
  2. <Действие> (значение атрибута и текстовое значение)
  3. (значение атрибута и текстовое значение)

Я в некотором роде могу сделать это с помощью циклов for внутри циклов for.но я надеялся на более чистое решение xpath, которое я так и не понял, как поступить с пространством имен.

Любые предложения будут высоко оценены.

1 Ответ

1 голос
/ 20 июня 2019

Здесь (собирая подмножество упомянутых вами элементов - добавьте больше кода для сбора остальных элементов)

import xml.etree.ElementTree as ET
import re

xmlstring = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns="http://fakeurl.com/page">
    <Alarm> 
        <Node>
            <location>Texas></location>
            <name>John</name>
        </Node>
    </Alarm>

    <Alarm> 
        <CreateTime>01/01/2011</CreateTime>

        <Story>
            <Node>
                <Name>Ethan</Name>
                <Address category="residential">
                    <address>1421 Morning SE</address>
                </Address>
            </Node>
        </Story>

        <Build>
            <Action category="build_value_1">Build was successful</Action>
        </Build>
        <OtherData type="string" meaning="favoriteTVShow">Purple</OtherData>
        <OtherData type="string" meaning="favoriteColor">Seinfeld</OtherData>
    </Alarm>
</root>'''

xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)

root = ET.fromstring(xmlstring)
alarms = root.findall('Alarm')
alarms_list = []
for alarm in alarms:
    create_time = alarm.find('CreateTime')
    if create_time is not None:
        entry = {'create_time': create_time.text}
        alarms_list.append(entry)
        actions = alarm.findall('Build/Action')
        if actions:
            entry['builds'] = []
        for action in actions:
            entry['builds'].append({'category': action.attrib['category'], 'status': action.text})

print(alarms_list)
...