Excluir nó XML contendo determinado elemento

4

Desejo remover todos os marcadores de um arquivo KML que contenha o elemento <tessellate> . O bloco a seguir deve ser totalmente removido:

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

Eu tentei regex perl não-ganancioso sem sorte (muitas coisas são removidas junto com o primeiro <Placemark> ):

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

Eu acredito que um analisador de XML é o caminho a percorrer, mas eu li a documentação do xmlstarlet e não cheguei a lugar nenhum. Portanto, qualquer solução em xmlstarlet, python etc. também é bem-vinda!

    
por Teresa e Junior 12.04.2013 / 08:02

3 respostas

5

com xmlstarlet :

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

E como kml usa namespaces, você precisa defini-lo primeiro (consulte a documentação do xmlstarlet )

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

Com perl , você precisaria processar o arquivo como um todo (não linha por linha) e adicionar o s flag a s/// . E mesmo assim, mesmo com correspondência não desejada, ainda corresponderia do primeiro <Placemark> até o próximo </Placemark> que ocorre após o próximo <tessellate> . Então você precisaria escrever algo como:

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

Dado este arquivo de teste:

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

Se você perl -0 -pe 's|<Placemark>.*?<tessellate>.*?</Placemark>||gs' sugeriu que removerá muito:

start

middle1

end

Isso ocorre porque o regex está apenas ansioso. Ele encontra uma tag de início, leva tudo até a primeira tag tessellate e até a próxima tag de finalização. Infelizmente ele não se importa se consome mais tags iniciais no caminho ...

Se você quiser fazer isso com regexes, você tem que processar cada bloco por conta própria: perl -0 -pe 's|<Placemark>.*?</Placemark>|$&=~/<tessellate>/?"":$&|gse'

Isso deve dar o resultado desejado.

    
por 12.04.2013 / 08:49
4

Usando o Python (2.7) com módulos padrão:

arquivo 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>

E o programa:

#! /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))

fornece como saída:

<Container>
<Placemark>
  <KeepMe/>
</Placemark>
</Container>
    
por 12.04.2013 / 09:18