Поиск и удаление элемента с помощью элемента elementTree в Python
У меня есть документ XML, в котором я хочу найти некоторые элементы и если они соответствуют некоторым критериям
Я хотел бы удалить их
Однако я не могу получить доступ к родительскому элементу элемента, чтобы удалить его
file = open('test.xml', "r")
elem = ElementTree.parse(file)
namespace = "{http://somens}"
props = elem.findall('.//{0}prop'.format(namespace))
for prop in props:
type = prop.attrib.get('type', None)
if type == 'json':
value = json.loads(prop.attrib['value'])
if value['name'] == 'Page1.Button1':
#here I need to access the parent of prop
# in order to delete the prop
Есть ли способ сделать это?
Спасибо
Ответы
Ответ 1
Вы можете удалить дочерние элементы с помощью метода remove
. Чтобы удалить элемент, вы должны вызвать его родительский метод remove
. К сожалению, Element
не дает ссылки на своих родителей, поэтому вам следует следить за отношениями между родителями и детьми (что говорит против вашего использования elem.findall()
)
Предлагаемое решение может выглядеть так:
root = elem.getroot()
for child in root:
if child.name != "prop":
continue
if True:# TODO: do your check here!
root.remove(child)
PS: не используйте prop.attrib.get()
, используйте prop.get()
, как описано здесь.
Ответ 2
Вы можете использовать xpath для выбора родителя элемента.
file = open('test.xml', "r")
elem = ElementTree.parse(file)
namespace = "{http://somens}"
props = elem.findall('.//{0}prop'.format(namespace))
for prop in props:
type = prop.get('type', None)
if type == 'json':
value = json.loads(prop.attrib['value'])
if value['name'] == 'Page1.Button1':
# Get parent and remove this prop
parent = prop.find("..")
parent.remove(prop)
http://docs.python.org/2/library/xml.etree.elementtree.html#supported-xpath-syntax
За исключением, если вы попробуете, чтобы он не работал: http://elmpowered.skawaii.net/?p=74
Поэтому вместо этого вы должны:
file = open('test.xml', "r")
elem = ElementTree.parse(file)
namespace = "{http://somens}"
search = './/{0}prop'.format(namespace)
# Use xpath to get all parents of props
prop_parents = elem.findall(search + '/..')
for parent in prop_parents:
# Still have to find and iterate through child props
for prop in parent.findall(search):
type = prop.get('type', None)
if type == 'json':
value = json.loads(prop.attrib['value'])
if value['name'] == 'Page1.Button1':
parent.remove(prop)
Это два поиска и вложенный цикл. Внутренний поиск находится только на элементах, которые, как известно, содержат реквизит в качестве первых детей, но это может не означать многого в зависимости от вашей схемы.
Ответ 3
Используя тот факт, что каждый ребенок должен иметь родителя, я собираюсь упростить пример @kitsu.eb. f используя команду findall для получения детей и родителей, их индексы будут эквивалентными.
file = open('test.xml', "r")
elem = ElementTree.parse(file)
namespace = "{http://somens}"
search = './/{0}prop'.format(namespace)
# Use xpath to get all parents of props
prop_parents = elem.findall(search + '/..')
props = elem.findall('.//{0}prop'.format(namespace))
for prop in props:
type = prop.attrib.get('type', None)
if type == 'json':
value = json.loads(prop.attrib['value'])
if value['name'] == 'Page1.Button1':
#use the index of the current child to find
#its parent and remove the child
prop_parents[props.index[prop]].remove(prop)
Ответ 4
Я знаю, что это старый поток, но это продолжало появляться, когда я пытался найти аналогичную задачу. Мне не понравился принятый ответ по двум причинам:
1) Он не обрабатывает несколько вложенных уровней тегов.
2) Он сломается, если несколько тегов xml будут удалены на одном уровне один за другим. Поскольку каждый элемент является индексом Element._children
, вы не должны удалять его при повторном итерации.
Я думаю, что более универсальным решением является следующее:
import xml.etree.ElementTree as et
file = 'test.xml'
tree = et.parse(file)
root = tree.getroot()
def iterator(parents, nested=False):
for child in reversed(parents):
if nested:
if len(child) >= 1:
iterator(child)
if True: # Add your entire condition here
parents.remove(child)
iterator(root, nested=True)
Для OP это должно работать, но у меня нет данных, с которыми вы работаете, чтобы проверить, идеально ли это.
import xml.etree.ElementTree as et
file = 'test.xml'
tree = et.parse(file)
namespace = "{http://somens}"
props = tree.findall('.//{0}prop'.format(namespace))
def iterator(parents, nested=False):
for child in reversed(parents):
if nested:
if len(child) >= 1:
iterator(child)
if prop.attrib.get('type') == 'json':
value = json.loads(prop.attrib['value'])
if value['name'] == 'Page1.Button1':
parents.remove(child)
iterator(props, nested=True)
Ответ 5
Мне нравится использовать выражение XPath для такого рода фильтрации. Если я не знаю иначе, такое выражение должно применяться на уровне корня, а это значит, что я не могу просто получить родителя и применить одно и то же выражение для этого родителя. Однако мне кажется, что есть хорошее и гибкое решение, которое должно работать с любым поддерживаемым XPath, если ни один из искомых узлов не является корнем. Это происходит примерно так:
root = elem.getroot()
# Find all nodes matching the filter string (flt)
nodes = root.findall(flt)
while len(nodes):
# As long as there are nodes, there should be parents
# Get the first of all parents to the found nodes
parent = root.findall(flt+'/..')[0]
# Use this parent to remove the first node
parent.remove(nodes[0])
# Find all remaining nodes
nodes = root.findall(flt)