Удалить шаблон из XML с помощью Python Etree

1

У меня есть xml файл:

<?xml version="1.0" encoding="UTF-8"?>
<kw name="k1" library="k1">
    <kw name="k2" library="k2">
        <kw name="Keep This" library="Keep This">
            <c name="c4" library="c4">
            </c>
        </kw>
        <kw name="k3" library="k3">
            <c name="c4" library="c4">
            </c>
        </kw>
        <c name="c3" library="c3">
            <c name="c4" library="c4">
            </c>
        </c>
    </kw>
</kw>

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

  1. tag= kw и атрибут содержит "Keep This"
  2. Эти теги не kw

Другая таблица должна быть удалена из xml

Таким образом, вывод должен выглядеть следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<kw name="k1" library="k1">
    <kw name="k2" library="k2">
        <kw name="Keep This" library="Keep This">
            <c name="c4" library="c4">
            </c>
        </kw>
        <c name="c3" library="c3">
            <c name="c4" library="c4">
            </c>
        </c>
    </kw>
</kw>

Очень сложно отслеживать рекурсивную функцию, может ли кто-нибудь помочь мне или порекомендовать другой способ для выполнения моих требований?

import xml.etree.ElementTree as ET
tree = ET.parse('a.xml')
root = tree.getroot()


def check(root):
    # if subchild exist "kw" tag, parse to the subchild
    if 'kw' in ([child.tag for child in root]):
        for child in root:
            flag = check(child)
            # remove
            if not flag:
                root.remove(child)
    # if subchild dose not exist "kw" tag
    else:
        if root.tag == 'kw':
            # Check if itself tag is kw and "Keep this"
            if 'Keep This' in [root.attrib[child] for child in root.attrib]:
                return True
            # Remove if itself tag is kw but without "Keep this"
            else:
                print ('remove')
                return False
        else:
            return True

check(root)

ET.dump(root)
  • 0
    Поскольку вы хотите отобразить xml на xml, это действительно работа для xslt.
  • 1
    Рассмотрим XSLT с Identity Transform и однострочным шаблоном. Смотрите демо . Python может запускать сценарии XSLT 1.0 с lxml или внешними процессорами .
Теги:
recursion
parsing

1 ответ

1
Лучший ответ

Вместо этого вы можете использовать следующую рекурсивную функцию. Обратите внимание на использование исключения в качестве способа уведомить родителя об удалении дочернего элемента, поскольку удаление узла должно выполняться из родителя, а возвращаемое значение Boolean указывает только, является ли потомком с тегом kw и значением атрибута Keep This найдено. Это имеет смысл уведомлять вызывающего абонента, когда в корневом узле отсутствует узел "держать", который, согласно правилу, должен быть удален, но не может быть потому, что он является корневым узлом:

import xml.etree.ElementTree as ET

def check(node):
    if node.tag == 'kw' and any(value == 'Keep This' for value in node.attrib.values()):
        return True
    keep = False
    removals = []
    for child in node:
        try:
            if check(child):
                keep = True
        except RuntimeError:
            removals.append(child)
    for child in removals:
        node.remove(child)
    if node.tag == 'kw' and not keep:
        raise RuntimeError('No "keep" node found under this node')
    return keep

tree = ET.parse('a.xml')
root = tree.getroot()
check(root)
ET.dump(root)

С помощью вашего ввода сэмпла эти выходы:

<kw library="k1" name="k1">
    <kw library="k2" name="k2">
        <kw library="Keep This" name="Keep This">
            <c library="c4" name="c4">
            </c>
        </kw>
        <c library="c3" name="c3">
            <c library="c4" name="c4">
            </c>
        </c>
    </kw>
</kw>
  • 0
    Спасибо, что потратили время на это, ваш ответ идеально подходит для этого ввода, но если у него есть секунда <kw> без «Keep This» в том же слое, он не будет удален. Я думаю, это потому, что метод remove () разрушит исходную структуру, и это действие for повлияет на цикл for. Так что он не будет путешествовать по всем узлам в этом слое.
  • 0
    Хороший звонок. Я исправил это, отложив удаления до тех пор, пока не будет завершена обработка всех дочерних элементов.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню