使用Python ElementTree如何向带命名空间的XML树添加节点?
XML命名空间下添加子节点失败的问题排查与解决
问题描述
我要给XML树添加节点,使用的命名空间是
xmlns:ns0="http://someplace.net/xml/"
插入子节点前已经通过findall()获取到了父节点,父节点内容如下:<ns0:parent xmlns:ns0="http://someplace.net/xml/" someattrib="some value">我尝试了三种构造子节点的方式:
node = ET.Element('mytag')
2. ```python node = ET.Element('ns0:mytag')
node = ET.Element('ns0:mytag')
node.set('xmlns:ns0', "http://someplace.net/xml/")之后执行`parent.extend(node)`,但最终树里完全找不到这个节点,三种方法都没用,请问我哪里错了?
问题根源与解决方案
其实你踩了xml.etree.ElementTree处理命名空间的一个常见坑:只写前缀没用,必须把前缀和对应的命名空间URI绑定起来,不然ET根本不知道这个前缀对应哪个命名空间,自然无法正确将节点纳入目标命名空间。
正确的两种实现方式
方式一:直接用命名空间URI创建元素
这种方式不需要纠结前缀,直接用URI来定义元素的命名空间,序列化时会自动沿用父节点的前缀:
# 先把命名空间URI存成变量,方便复用 NS_URI = "http://someplace.net/xml/" # 创建带命名空间的子节点,注意URI要用大括号包裹 node = ET.Element(f'{{{NS_URI}}}mytag') # 单个节点用append更合适,extend是给元素列表用的 parent.append(node)
方式二:先注册前缀再创建元素
如果你想保留ns0:的前缀写法,需要先注册前缀和URI的映射,让ET能识别这个前缀:
# 注册命名空间前缀与URI的对应关系 ET.register_namespace('ns0', "http://someplace.net/xml/") # 现在再创建带前缀的节点,ET就知道它属于哪个命名空间了 node = ET.Element('ns0:mytag') parent.append(node)
为什么你的三种方法都不行?
- 第一种方法:创建的是无命名空间的
mytag节点,和父节点的ns0命名空间完全不搭边,后续如果用带命名空间的路径查找(比如findall('.//ns0:mytag')),自然找不到它。 - 第二种方法:
ns0:mytag会被ET当成一个完整的标签名(而不是“ns0前缀下的mytag”),本质还是无命名空间节点,只是名字里多了个冒号,和父节点的命名空间不匹配。 - 第三种方法:手动加
xmlns:ns0属性只是给节点添了个命名空间声明,但创建节点时的ns0:mytag依然没和URI绑定,ET还是不认为它属于目标命名空间,相当于做了无用功。
另外提个小细节:你用parent.extend(node)其实不太对,extend是用来添加多个节点(比如一个元素列表)的,单个节点用parent.append(node)才是规范写法,不过这不是你找不到节点的核心原因,但改过来会更合理。
内容的提问来源于stack exchange,提问作者kakyo
相关产品推荐
相关产品推荐

