Python使用ElementTree解析XML时移除非DVC元素失败求助
问题分析与解决
你遇到的问题是在遍历ElementTree的子元素时直接修改父节点的子元素列表,导致部分元素被跳过,这也是处理列表迭代时的常见陷阱。
问题根源
当你用for child in root:遍历root的子元素时,Python是在迭代root子元素列表的实时视图。如果在遍历过程中移除元素,列表长度会动态变化,迭代器会直接跳过下一个元素。比如原根节点的子元素顺序是[TSK, PFD, FRM, CTR, CPC, DVC],当你移除第一个元素TSK后,列表变成[PFD, FRM, CTR, CPC, DVC],迭代器接下来会取索引1的元素(也就是原来的FRM),直接跳过了PFD,这就是为什么这些非DVC元素没被完全移除的原因。
修正后的代码
解决方法很简单:遍历子元素的副本,这样即使原列表被修改,迭代的是独立的副本,不会影响遍历的完整性:
import xml.etree.ElementTree as etree exp_root = etree.parse(dm_files_path + "\\Export\\" + display_name + "\\TASKDATA.XML") root = exp_root.getroot() # 用list(root)创建当前子元素的副本,遍历副本而非原列表 for child in list(root): if child.tag != 'DVC': root.remove(child) # 写入时指定编码和XML声明,保持和原文件一致的格式 exp_root.write(dm_files_path + "\\Export\\" + display_name + '\\TASKDATA_Disp.XML', encoding='UTF-8', xml_declaration=True)
验证效果
用你提供的XML示例测试,修正后的代码会移除所有非DVC的根节点子元素,最终输出的XML只会保留<DVC>元素及其内部子节点:
<?xml version='1.0' encoding='UTF-8'?> <TaskData VersionMajor="4" VersionMinor="2" Taskr="" Version="201" DataTransferOrigin="2"> <DVC A="DVC-1" B="Sprayer" C="02.00.01 " D="A00C80000C40B6BC" E=" 30397040 Z12338" F="3037313131464C" G="FF565A005F6E65"> <DVP A="6001" B="0" C="0.2641719878" D="0" E="gal"/> <DVP A="6003" B="0" C="0.0002471050" D="2" E="ac"/> <DVP A="6004" B="0" C="0.0000006214" D="2" E="mi"/> <DVP A="6005" B="0" C="0.0002777780" D="2" E="hours"/> <DVP A="6006" B="0" C="0.0393701009" D="0" E="in"/> <DVP A="6008" B="0" C="0.0002641720" D="2" E="gal"/> <DVP A="6010" B="0" C="0.0000106907" D="4" E="gal/ac"/> <DPT A="4003" B="009D" C="2" D="Connector Type"/> <DPT A="4484" B="00B3" C="3" D="Operation Type (ACP)"/> </DVC> </TaskData>
补充(可选)
如果后续需要递归移除所有层级的非DVC元素(不仅是根节点的直接子元素),可以用递归函数处理:
def remove_non_dvc_elements(node): # 同样遍历子元素副本 for child in list(node): if child.tag != 'DVC': node.remove(child) else: # 递归处理DVC内部的子元素(如果需要清理DVC下的非目标元素) remove_non_dvc_elements(child) remove_non_dvc_elements(root)
不过从你的需求来看,只需要处理根节点的直接子元素,第一种方法就完全够用了。
内容的提问来源于stack exchange,提问作者Tim Patton
相关产品推荐
相关产品推荐

