Как удалить определенный тег, который может быть пустым в файле xml

Я пытаюсь удалить определенный тег из XML-файла, но только если он пуст.

файл:

<?xml version="1.0" encoding="utf-8"?>
<parent>
  <child>
    <value1>Foo<value1/>
    <value2>Bar<value2/>
    <value3>Hello World<value3/>
    <value3/>
    <value3/>
    <value3/>
  <child/>
<parent/>

ожидаемый результат:

<?xml version="1.0" encoding="utf-8"?>
<parent>
  <child>
    <value1>Foo<value1/>
    <value2>Bar<value2/>
    <value3>Hello World<value3/>
  <child/>
<parent/>

У меня проблемы с чтением файла и его разбором с помощью lxml, поэтому я открыт для любых других методов/модулей python3. в идеале хотелось бы, чтобы код делал что-то вроде следующего:

def remove_empty_tag(tag=tagname, file=data):
   ...

data = open("file.xml").read()
new_xml = remove_empty_tag(tag="value3", data)
print(new_xml)

но открыты для любой помощи на самом деле или даже направления.


person Marco Fernandes    schedule 03.06.2019    source источник


Ответы (2)


Вам не нужно open() файл для чтения или записи; используйте функцию lxml parse() для анализа файла и write(), чтобы написать новый.

Вы также должны иметь возможность использовать ось self:: xpath вместо python if для проверки имени тега.

Пример...

Ввод XML (old.xml)

<parent>
  <child>
    <value1>Foo</value1>
    <value2>Bar</value2>
    <value3>Hello World</value3>
    <value3/>
    <value3/>
    <value3/>
  </child>
</parent>

Питон

from lxml import etree


def remove_empty_tag(tag, original_file, new_file):
    root = etree.parse(original_file)
    for element in root.xpath(f".//*[self::{tag} and not(node())]"):
        element.getparent().remove(element)

    # Serialize "root" and create a new tree using an XMLParser to clean up
    # formatting caused by removing elements.
    parser = etree.XMLParser(remove_blank_text=True)
    tree = etree.fromstring(etree.tostring(root), parser=parser)
    # Write to new file.
    etree.ElementTree(tree).write(new_file, pretty_print=True, xml_declaration=True, encoding="utf-8")


remove_empty_tag("value3", "old.xml", "new.xml")

Вывод XML (new.xml)

<?xml version='1.0' encoding='UTF-8'?>
<parent>
  <child>
    <value1>Foo</value1>
    <value2>Bar</value2>
    <value3>Hello World</value3>
  </child>
</parent>

Примечание. Сериализация и создание нового дерева не являются строго обязательными. Вы могли бы просто сделать это вместо этого:

root.write(new_file, pretty_print=True, xml_declaration=True, encoding="utf-8")

но форматирование вывода будет немного другим (обратите внимание на дополнительный отступ конечного тега child:

<?xml version='1.0' encoding='UTF-8'?>
<parent>
  <child>
    <value1>Foo</value1>
    <value2>Bar</value2>
    <value3>Hello World</value3>
    </child>
</parent>
person Daniel Haley    schedule 03.06.2019
comment
Спасибо. Это гораздо более чистый способ ведения дел по сравнению с моим собственным ответом. Оба достигают одинаковых результатов. - person Marco Fernandes; 04.06.2019

from lxml import etree


def remove_empty_tag(tag, original_file, new_file):
    file = open(original_file, 'r', encoding='utf8').read()
    root = etree.fromstring(file)
    for element in root.xpath(".//*[not(node())]"):
        if element.tag == tag:
            element.getparent().remove(element)
    with open(new_file, 'wb') as f:
        f.write(etree.tostring(root, pretty_print=True))


remove_empty_tag("value3", "old.xml", "new.xml")

Это то, чего я пытался добиться, и по какой-то причине он жалуется на файл/данные, если в нем есть <?xml version="1.0" encoding="utf-8"?>. Так что просто удалите его, и это исправлено. На самом деле это не дубликат, потому что в ответе из другого потока не указано, как удалить только определенный пустой тег, и нет помощи в том, что он на самом деле делает или как записать его в новый файл без случайного '\n' везде...

person Marco Fernandes    schedule 03.06.2019
comment
Что вы имеете в виду под жалобами? Каково фактическое сообщение об ошибке/предупреждении, которое вы получаете? Вам не нужно удалять объявление XML, чтобы заставить его работать. (Тем не менее, ваш пример XML имеет неправильный формат, потому что все ваши конечные теги неверны; их нужно будет исправить в первую очередь.) - person Daniel Haley; 03.06.2019
comment
Пример, который я привел, был бы неправильным, да, потому что я пытался продемонстрировать, чего я хотел достичь с помощью быстрых фиктивных данных. Я не хочу использовать личные данные, задавая вопрос об этой платформе. Сообщение, которое я продолжал получать с первой строкой в ​​xml, было ValueError: Unicode strings with encoding declaration are not supported., и я просто удалил его перед обработкой и вернул обратно (не вручную, а с некоторыми манипуляциями со строками). - person Marco Fernandes; 04.06.2019