Сортировать XML-файл по тексту тега с помощью Python - PullRequest
0 голосов
/ 08 июля 2019

У меня есть XML-файл, подобный этому:

<annotation>

        <object>
        <name>medium</name>
        <pose>Left</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>267</xmin>
            <ymin>273</ymin>
            <xmax>415</xmax>
            <ymax>324</ymax>
        </bndbox>
    </object>
    <object>
        <name>medium</name>
        <pose>Left</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>105</xmin>
            <ymin>229</ymin>
            <xmax>261</xmax>
            <ymax>292</ymax>
        </bndbox>
    </object>

</annotation>

Я хочу отсортировать этот xml, используя текст тега ymin в порядке возрастания.
Я пытаюсь это с помощью следующего кода, который выбрасывает объект 'NoneType' не повторяется .

def getkey(elem):
    return elem.findtext("ymin")

tree = ET.parse("Train/5.xml")
container = tree.find("bndbox")           
container[:] = sorted(container, key=getkey)

Я хочу, чтобы второй тег объекта отображался вместо первого тега объекта в конечном результате.
Как мне этого добиться?

Ответы [ 2 ]

0 голосов
/ 08 июля 2019

С использованием BeautifulSoup:

data = '''

<annotation>

        <object>
        <name>medium</name>
        <pose>Left</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>267</xmin>
            <ymin>273</ymin>
            <xmax>415</xmax>
            <ymax>324</ymax>
        </bndbox>
    </object>
    <object>
        <name>medium</name>
        <pose>Left</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>105</xmin>
            <ymin>229</ymin>
            <xmax>261</xmax>
            <ymax>292</ymax>
        </bndbox>
    </object>

</annotation>'''

from bs4 import BeautifulSoup
soup = BeautifulSoup(data, 'xml')
soup.annotation.contents = sorted(soup.annotation.select('object'), key=lambda k: int(k.select_one('ymin').text))


# For XML pretty print, sorted XML is inside `soup`
from xml.dom import minidom
xmlstr = minidom.parseString(str(soup)).toprettyxml(indent="  ").replace('\n\n', '').strip()
print(xmlstr)

Печать:

<?xml version="1.0" ?>
<annotation>
  <object>
        <name>medium</name>
        <pose>Left</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>105</xmin>
            <ymin>229</ymin>
            <xmax>261</xmax>
            <ymax>292</ymax>
          </bndbox>
      </object>
  <object>
        <name>medium</name>
        <pose>Left</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>267</xmin>
            <ymin>273</ymin>
            <xmax>415</xmax>
            <ymax>324</ymax>
          </bndbox>
      </object>
</annotation>

РЕДАКТИРОВАНИЕ (с чтением / записью из / в файл XML):

from bs4 import BeautifulSoup

with open('file.xml', 'r') as f_in:
    soup = BeautifulSoup(f_in.read(), 'xml')

soup.annotation.contents = sorted(soup.annotation.select('object'), key=lambda k: int(k.select_one('ymin').text))


# For XML pretty print, sorted XML is inside `soup`
from xml.dom import minidom
xmlstr = minidom.parseString(str(soup)).toprettyxml(indent="  ").replace('\n\n', '').strip()

with open('file_out.xml', 'w') as f_out:
    f_out.write(xmlstr)
0 голосов
/ 08 июля 2019

Вы можете использовать iter для рекурсивного поиска ymin, затем clear существующих объектов и повторно добавить отсортированный список из object s:

objects = sorted(tree.findall('object'),
                 key=lambda object_node: int(next(object_node.iter('ymin')).text))

tree.clear()
tree.extend(objects)

print(ET.tostring(tree))

Outputs

<annotation>
    <object>
        <name>medium</name>
        <pose>Left</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>105</xmin>
            <ymin>229</ymin>
            <xmax>261</xmax>
            <ymax>292</ymax>
        </bndbox>
    </object>
    <object>
        <name>medium</name>
        <pose>Left</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>267</xmin>
            <ymin>273</ymin>
            <xmax>415</xmax>
            <ymax>324</ymax>
        </bndbox>
    </object>
</annotation>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...