刪除包含特定元素的 XML 節點

刪除包含特定元素的 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 等解決方案!

答案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,您需要將文件作為一個整體(而不是逐行)處理並將標誌添加ss///.即使這樣,即使使用非貪婪匹配,它仍然會從第一個匹配到下一個之後發生的下<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>

相關內容