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

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

KMLファイルから要素を含むすべてのPlacemarkを削除したい<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 などのソリューションも歓迎します。

答え1

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'

答え2

次のテストファイルがあるとします:

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'

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

答え3

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

関連情報