Удалить узел XML, содержащий определенный элемент

Я хочу удалить все метки из файла KML, содержащие элемент <tessellate> . Следующий блок должен быть полностью удален:

 <Placemark> <styleUrl>#m_ylw-pushpin330</styleUrl> <LineString> <tessellate>1</tessellate> <coordinates> 0.0000000000000,0.0000000000000,0 0.0000000000000,0.0000000000000,0 </coordinates> </LineString> </Placemark> 

Я пробовал некоторое нежеланное регулярное выражение perl без везения (много вещей удаляется вместе с первой <Placemark> ):

 sed -r ':a; N; $!ba; s/\n\t*//g' myplaces.kml | perl -pe 's|<Placemark>.*?<tessellate>.*?</Placemark>||g' 

Я считаю, что XML-парсер – это путь, но я прочитал документацию для xmlstarlet и не получил нигде. Поэтому любые решения в xmlstarlet, python и т. Д. Также приветствуются!

С xmlstarlet :

 xmlstarlet ed -d '//Placemark[.//tessellate]' < myplaces.kml 

И поскольку kml использует пространства имен, вы должны сначала определить его (см. Документацию xmlstarlet )

 xmlstarlet ed -N 'ns=http://www.opengis.net/kml/2.2' -d '//ns:Placemark[.//ns:tessellate]' 

С perl вам нужно обработать файл в целом (не по строкам) и добавить флаг s/// в s/// . И даже тогда, даже при не-жадном совпадении, все равно будет соответствовать первая <Placemark> следующая </Placemark> которая возникает после следующего <tessellate> . Поэтому вам нужно написать что-то вроде:

 perl -0777 -pe 's|(<Placemark>.*?</Placemark>)| $1 =~ /<tessellate>/?"":$1|gse' 

Учитывая этот тестовый файл:

 start <Placemark> <tessellate>1</tessellate> </Placemark> middle1 <Placemark> </Placemark> middle2 <Placemark> <tessellate>1</tessellate> </Placemark> end 

Если вы делаете perl -0 -pe 's|<Placemark>.*?<tessellate>.*?</Placemark>||gs' как вы предположили, он слишком сильно удалит:

 start middle1 end 

Это связано с тем, что регулярное выражение только смотрит вперед. Он находит начальный тег, занимает все до первого тесселета тесселяции и до следующего тега конца. К несчастью, это не волнует, если он потребляет больше стартовых меток на пути …

Если вы хотите сделать это с помощью регулярных выражений, вам нужно обработать каждый блок самостоятельно: perl -0 -pe 's|<Placemark>.*?</Placemark>|$&=~/<tessellate>/?"":$&|gse'

Это должно дать желаемый результат.

Использование Python (2.7) со стандартными модулями:

Файл test.xml :

 <Container> <Placemark> <KeepMe/> </Placemark> <Placemark> <styleUrl>#m_ylw-pushpin330</styleUrl> <LineString> <tessellate>1</tessellate> <coordinates> 0.0000000000000,0.0000000000000,0 0.0000000000000,0.0000000000000,0 </coordinates> </LineString> </Placemark> </Container> 

И программа:

 #! /usr/bin/env python from __future__ import print_function # works on 2.x and 3.x from lxml import etree file_name = 'test.xml' root = etree.parse(file_name) for element in root.iterfind('.//Placemark'): if(element.find('.//tessellate')) is not None: element.getparent().remove(element) print(etree.tostring(root)) 

дает в качестве результата:

 <Container> <Placemark> <KeepMe/> </Placemark> </Container>