web-dev-qa-db-ja.com

特定の要素を含むXMLノードを削除する

要素<tessellate>を含むKMLファイルからすべての目印を削除したい。次のブロックはwhollyを削除する必要があります。

<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などのソリューションも歓迎します!

4
Teresa e Junior

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を使用する場合は、ファイル全体を(1行ずつではなく)処理し、sフラグをs///に追加する必要があります。そしてそれでも、貪欲でない一致があっても、最初の<Placemark>から次の</Placemark>まで、次の<tessellate>の後に発生します。したがって、次のように記述する必要があります。

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

このテストファイルを考えると:

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'

これにより、望ましい結果が得られるはずです。

4
michas

標準モジュールで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>
4
Anthon