详解在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>