详解在Python程序中解析并修改XML内容的方法
需求
在实际应用中,需要对xml配置文件进行实时修改,
1.增加、删除某些节点
2.增加,删除,修改某个节点下的某些属性
3.增加,删除,修改某些节点的文本
使用xml文档
<?xmlversion="1.0"encoding="UTF-8"?> <framework> <processers> <processername="AProcesser"file="lib64/A.so" path="/tmp"> </processer> <processername="BProcesser"file="lib64/B.so"value="fordelete"> </processer> <processername="BProcesser"file="lib64/B.so2222222"/> <services> <servicename="search"prefix="/bin/search?" output_formatter="OutPutFormatter:service_inc"> <chainsequency="chain1"/> <chainsequency="chain2"></chain> </service> <servicename="update"prefix="/bin/update?"> <chainsequency="chain3"value="fordelete"/> </service> </services> </processers> </framework>
实现思想
使用ElementTree,先将文件读入,解析成树,之后,根据路径,可以定位到树的每个节点,再对节点进行修改,最后直接将其输出
实现代码
#!/usr/bin/python #-*-coding=utf-8-*- #author:wklken@yeah.net #date:2012-05-25 #version:0.1 fromxml.etree.ElementTreeimportElementTree,Element defread_xml(in_path): '''读取并解析xml文件 in_path:xml路径 return:ElementTree''' tree=ElementTree() tree.parse(in_path) returntree defwrite_xml(tree,out_path): '''将xml文件写出 tree:xml树 out_path:写出路径''' tree.write(out_path,encoding="utf-8",xml_declaration=True) defif_match(node,kv_map): '''判断某个节点是否包含所有传入参数属性 node:节点 kv_map:属性及属性值组成的map''' forkeyinkv_map: ifnode.get(key)!=kv_map.get(key): returnFalse returnTrue #---------------search----- deffind_nodes(tree,path): '''查找某个路径匹配的所有节点 tree:xml树 path:节点路径''' returntree.findall(path) defget_node_by_keyvalue(nodelist,kv_map): '''根据属性及属性值定位符合的节点,返回节点 nodelist:节点列表 kv_map:匹配属性及属性值map''' result_nodes=[] fornodeinnodelist: ifif_match(node,kv_map): result_nodes.append(node) returnresult_nodes #---------------change----- defchange_node_properties(nodelist,kv_map,is_delete=False): '''修改/增加/删除节点的属性及属性值 nodelist:节点列表 kv_map:属性及属性值map''' fornodeinnodelist: forkeyinkv_map: ifis_delete: ifkeyinnode.attrib: delnode.attrib[key] else: node.set(key,kv_map.get(key)) defchange_node_text(nodelist,text,is_add=False,is_delete=False): '''改变/增加/删除一个节点的文本 nodelist:节点列表 text:更新后的文本''' fornodeinnodelist: ifis_add: node.text+=text elifis_delete: node.text="" else: node.text=text defcreate_node(tag,property_map,content): '''新造一个节点 tag:节点标签 property_map:属性及属性值map content:节点闭合标签里的文本内容 return新节点''' element=Element(tag,property_map) element.text=content returnelement defadd_child_node(nodelist,element): '''给一个节点添加子节点 nodelist:节点列表 element:子节点''' fornodeinnodelist: node.append(element) defdel_node_by_tagkeyvalue(nodelist,tag,kv_map): '''同过属性及属性值定位一个节点,并删除之 nodelist:父节点列表 tag:子节点标签 kv_map:属性及属性值列表''' forparent_nodeinnodelist: children=parent_node.getchildren() forchildinchildren: ifchild.tag==tagandif_match(child,kv_map): parent_node.remove(child) if__name__=="__main__": #1.读取xml文件 tree=read_xml("./test.xml") #2.属性修改 #A.找到父节点 nodes=find_nodes(tree,"processers/processer") #B.通过属性准确定位子节点 result_nodes=get_node_by_keyvalue(nodes,{"name":"BProcesser"}) #C.修改节点属性 change_node_properties(result_nodes,{"age":"1"}) #D.删除节点属性 change_node_properties(result_nodes,{"value":""},True) #3.节点修改 #A.新建节点 a=create_node("person",{"age":"15","money":"200000"},"thisisthefirestcontent") #B.插入到父节点之下 add_child_node(result_nodes,a) #4.删除节点 #定位父节点 del_parent_nodes=find_nodes(tree,"processers/services/service") #准确定位子节点并删除之 target_del_node=del_node_by_tagkeyvalue(del_parent_nodes,"chain",{"sequency":"chain1"}) #5.修改节点文本 #定位节点 text_nodes=get_node_by_keyvalue(find_nodes(tree,"processers/services/service/chain"),{"sequency":"chain3"}) change_node_text(text_nodes,"newtext") #6.输出到结果文件 write_xml(tree,"./out.xml")
修改后的结果
<?xmlversion='1.0'encoding='utf-8'?> <framework> <processers> <processerfile="lib64/A.so"name="AProcesser"path="/tmp"> </processer> <processerage="1"file="lib64/B.so"name="BProcesser"> <personage="15"money="200000">thisisthefirestcontent</person> </processer> <processerage="1"file="lib64/B.so2222222"name="BProcesser"> <personage="15"money="200000">thisisthefirestcontent</person> </processer> <services> <servicename="search"output_formatter="OutPutFormatter:service_inc" prefix="/bin/search?"> <chainsequency="chain2"/> </service> <servicename="update"prefix="/bin/update?"> <chainsequency="chain3"value="fordelete">newtext</chain> </service> </services> </processers> </framework>