Python:在xml中,如何删除父节点中的节点

问题描述:

我有一个非常大的XML,我需要从删除一些特定的节点在另一个节点。 例如我有一个列表,其中包含应该出现在xml中的节点的名称。因此,除了这些节点之外,父节点中的所有其他节点都应该被删除并写入新的xml文件。Python:在xml中,如何删除父节点中的节点

我只需要删除节点,即“实例”,其中第一个“数据”元素不等于出现在我的名单,我会提供一个值。其余的xml信息,即'描述','符号'标签不应该被打扰。

假设:我已经解析了应该从外部文件读取到python列表变量的数据。

DOM或SAX任何人都可以。但我相信DOM非常快。 对于任何BIF的可用或逻辑的任何提示对我来说也是很好的。

注意:我是Python新手。所以请评论我的代码中是否有任何错误。

我下面的代码:

from xml.etree.ElementTree import ElementTree 
tree = ElementTree() 
tree.parse('Test.xml') 

file = open("File.txt") 

list = [] 

for lines in file: 
    list.append(lines) 

Instance = tree.findall('Instance') 
for i in Instance: 
    while (i != list[i]): 
     Instance.remove(i) 

tree.write('new.xml') 

下面是示例XML文件:

<?xml version='1.0' encoding='UTF-8'?> 
<Identification> 
<Description ID="12">Some text</Description> 
</Identification> 
<Symbols> 
    <Name Width="1">abc</Name> 
    <Name Width="2">def</Name> 
</Symbols> 

<Instance RowRef="A"> 
    <DataSet> 
    <Data>12345678</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
</Instance> 
<Instance RowRef="B"> 
    <DataSet> 
    <Data>87654321</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
</Instance> 
<Instance RowRef="C"> 
    <DataSet> 
    <Data>06354237/Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 

+1

向我们展示您的工作? – 2014-09-27 19:41:42

+0

@Vivek:我用我的代码更新了问题的详细信息。用我的代码,所有的xml标签都被替换为“ns0:”。我不知道为什么会发生这种情况。 – manty 2014-09-27 20:11:28

+0

'File.txt'的内容是什么? – Yoel 2014-09-27 20:29:11

您是数点的困惑。

第一个,您提供的xml缺少根标记。您的XML文件看起来应该更喜欢这个(其中Root可以与任何标签更换是必要的):

<?xml version='1.0' encoding='UTF-8'?> 
<Root> 
<Identification> 
<Description ID="12">Some text</Description> 
</Identification> 
<Symbols> 
    <Name Width="1">abc</Name> 
    <Name Width="2">def</Name> 
</Symbols> 

<Instance RowRef="A"> 
    <DataSet> 
    <Data>12345678</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
</Instance> 
<Instance RowRef="B"> 
    <DataSet> 
    <Data>87654321</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
</Instance> 
<Instance RowRef="C"> 
    <DataSet> 
    <Data>06354237</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
</Instance> 
</Root> 

,如果您担心速度,可以考虑使用cElementTree代替ElementTree

>>> import xml.etree.cElementTree as ET # use cElementTree for faster processing 

,你需要一个名称分配给ET.parse方法的结果,或者你就没有办法以后引用它:

>>> tree = ET.parse('Test.xml') 

,现在你需要找到树的根你会发现所有的树的Instance元素之前:

>>> root = tree.getroot() # now get the root 
>>> keeper_data = ['06354237', '87654321'] # your list that you will apparently get from a file? 
>>> instances = root.findall('Instance') 

现在,你需要找到Instance元件,其Data值表明,应该删除Instance元素:

第五个,您需要检查文本是否是第一个Data元素在你的门将名单,并第六,你从rootremove你的元素(或任何父恰好是),而不是从instances

>>> for instance in instances: 
     data1 = instance.find('./DataSet/Data') 
     if data1.text not in keeper_data: 
      # NOTE WELL: I remove from the root (not the instance) below! 
      root.remove(instance) 

现在写信给你的新的XML文件:

>>> tree.write('New.xml') 

你生成的XML文件看起来像这样:

<Root> 
<Identification> 
    <Description ID="12">Some text</Description> 
</Identification> 
<Symbols> 
    <Name Width="1">abc</Name> 
    <Name Width="2">def</Name> 
</Symbols> 
<Instance RowRef="B"> 
    <DataSet> 
    <Data>87654321</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
    </Instance> 
    <Instance RowRef="C"> 
    <DataSet> 
    <Data>06354237</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
    <DataSet> 
    <Data>abcd</Data> 
    </DataSet> 
    </Instance> 
</Root> 

不e实例的值为12345678的数据元素和其他keeper_data元素均未被省略。

+0

是的,你是对的,我在我的问题中没有提及哪些'数据'值与之比较。但是你猜对了,它只是第一个'数据'元素。但是你的这段代码也给了我这样的错误:“ValueError:list.remove(x):x not in list”。当我在'remove'语句之后再添加一个分隔符时,这个错误消失了,然后新的xml按照预期正常打印。但仍然是我的原始xml给了ValueError.Any想法可能是什么原因? – manty 2014-09-28 11:09:59

+0

@manty确保你的'else'语句直接落在'for'语句下,而* not *语句落在'if'语句下。 'else'语句只应在所有'Data'元素运行完毕后才运行。它属于'for'循环。如果你用'if'语句排列它的缩进,你可能会尝试删除一个'Instance'元素两次,并会得到你提到的错误。 – 2014-09-28 11:35:43

+0

@manty请注意,我也稍微修改了代码,因为您在上面的注释中指出,您只希望检查第一个'Data'元素的值与'keeper_list'对应的值。如果我的答案帮助您解决了问题,请考虑[接受它](http://meta.stackexchange.com/a/5235)作为正确答案。 – 2014-09-28 11:46:05