Сортировка XML документов с Python и ElementTree - PullRequest
0 голосов
/ 24 апреля 2020

Я пытаюсь реорганизовать некоторые xml файлы, которые содержат несколько сегментов полного маршрута, структурированного как:

<trk>
    <name>GPSRoute.XML</name>
    <trkseg>
        <trkpt lat="37.077882" lon="-112.242785">
            <ele>1688.00</ele>
            <time>2020-04-18T01:56:39.80Z</time>
        </trkpt>
        <extensions>
            <name>14</name>
            <gte:color>#00ce00</gte:color>
        </extensions>
    </trkseg>
    <trkseg>
        <trkpt lat="37.077888" lon="-112.242783">
            <ele>1688.00</ele>
            <time>2020-04-18T01:56:39.80Z</time>
        </trkpt>
        <extensions>
            <name>1</name>
            <gte:color>#00ce00</gte:color>
        </extensions>
    </trkseg>
</trk>

Я пытаюсь отсортировать файл по имени, а не по времени, как в настоящее время и запишите результат в новый файл. Пока это то, как далеко я продвинулся, он успешно захватывает имена в списке, но он вызывает ошибку data.sort () с:

"TypeError: '<' не поддерживается между экземплярами 'xml .etree.ElementTree.Element 'и' xml .etree.ElementTree.Element '"</p>

Если бы кто-нибудь мог указать мне правильное направление, это было бы очень полезно!

import xml.etree.ElementTree as ET

tree = ET.parse('Filename.xml')

root = tree.getroot()
data = []
for track in root:
    for segment in track:
        for extension in segment:
            for name in extension.findall('name'):
                print(name.text)
                data.append((name))
            data.sort()


tree.write('Sorted.xml')

Ответы [ 2 ]

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

Объект Element можно рассматривать как итерируемый с дочерними элементами в качестве членов. Это упрощает сортировку дочерних элементов root. В этом случае нам нужно сделать исключение для первого потомка (<name>GPSRoute.XML</name>), который не участвует в сортировке.

В документе XML есть необъявленный префикс пространства имен, поэтому, чтобы он работал, я изменил gte:color на color.

import xml.etree.ElementTree as ET

tree = ET.parse('Filename.xml')
root = tree.getroot()

# Temporarily remove the 'name' element
name = root.find("name")
root.remove(name)

# Sort the 'trkseg' elements using 'extensions/name' as key
root[:] = sorted(root, key=lambda trkseg: int(trkseg.findtext("extensions/name")))

# Put the 'name' element back
root.insert(0, name)

print(ET.tostring(root).decode())

Результат:

<trk>
  <name>GPSRoute.XML</name>
  <trkseg>
    <trkpt lat="37.077888" lon="-112.242783">
      <ele>1688.00</ele>
      <time>2020-04-18T01:56:39.80Z</time>
    </trkpt>
    <extensions>
      <name>1</name>
      <color>#00ce00</color>
    </extensions>
  </trkseg>
<trkseg>
    <trkpt lat="37.077882" lon="-112.242785">
      <ele>1688.00</ele>
      <time>2020-04-18T01:56:39.80Z</time>
    </trkpt>
    <extensions>
      <name>14</name>
      <color>#00ce00</color>
    </extensions>
  </trkseg>
  </trk>
0 голосов
/ 24 апреля 2020

Нет реального способа сортировки xml, пока вы не дойдете до xpath 3.1, я думаю, но это возможно, если вы решите этот вопрос.

Обратите внимание, что, поскольку xml в вашем вопросе неверно (у вас есть необъявленные пространства имен), я использовал более простительный html парсер. С вашим реальным кодом вы должны использовать синтаксический анализатор xml, как указано ниже.

Этот код выполняет сбор значений узлов каждого <name> дочернего узла (т. Е. Целевого числа) из каждого <trkseg> родительский узел, сохраняет их в список, сортирует список, использует отсортированный список, чтобы снова выбрать узлы <trkseg> в указанном порядке, и использует их (вместе с открывающими и закрывающими тегами) для создания нового xml.

import lxml.html as lh # with actual xml you would probably use "from lxml import etree"
trk = """your xml above"""

doc = lh.fromstring(trk) # with actual xml you should probably use "doc = etree.XML(trk)"

names = []
new_trk = """<trk>
    <name>GPSRoute.XML</name>""" # this is the preamble which is left untouched
for nam in doc.xpath('//extensions//name'):
    names.append(nam.text) #grab the numbers
for name in sorted(names): #sort the grabbed numbers
    target = doc.xpath(f'//trkseg[.//name/text()={name}]')
    for t in target:
        new_trk += lh.tostring(t).decode()
new_trk += '</trk>' # append the closing tag, which is also left untouched
print(new_trk)

Вывод:

<trk>
    <name>GPSRoute.XML</name><trkseg>
        <trkpt lat="37.077888" lon="-112.242783">
            <ele>1688.00</ele>
            <time>2020-04-18T01:56:39.80Z</time>
        </trkpt>
        <extensions>
            <name>1</name>
            <color>#00ce00</color>
        </extensions>
    </trkseg>
<trkseg>
        <trkpt lat="37.077882" lon="-112.242785">
            <ele>1688.00</ele>
            <time>2020-04-18T01:56:39.80Z</time>
        </trkpt>
        <extensions>
            <name>14</name>
            <color>#00ce00</color>
        </extensions>
    </trkseg>
    </trk>
...