删除包含特定元素的 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>

相关内容