我想选择特定元素的第一个子元素(subelement
),但是这个子元素的命名空间与父元素的命名空间不同。而且,这个孩子可以是任何命名空间。
xml = '''<root xmlns="default_ns">
<subelement>
<!-- here we can have an element of any namespace -->
<some_prefix:a xmlns:some_prefix="some_namespace">
<some_prefix:b/>
</some_prefix:a>
</subelement>
</root>'''
root = etree.fromstring(xml)
evaluator = etree.XPathEvaluator(root, namespaces={'def':'default_ns'})
child = evaluator.evaluate('//def:subelement/child::*')[0]
a_string = etree.tostring(child)
print a_string
这给出了:
<some_prefix:a xmlns:some_prefix="some_namespace" xmlns="default_ns">
<some_prefix:b/>
</some_prefix:a>
但我想得的是没有来自父xmlns="default_ns"
的名称空间声明的孩子:
<some_prefix:a xmlns:some_prefix="some_namespace">
<some_prefix:b/>
</some_prefix:a>
答案 0 :(得分:1)
但我想得到的是没有名称空间声明的孩子 parent xmlns =“default_ns”。
仅通过评估XPath表达式无法实现。
在XML中,任何元素都会继承其所有父级的命名空间节点,除非它重新定义了特定的命名空间。
这意味着some_prefix:a
从其父("default_ns"
)继承默认命名空间subelement
,它本身从顶部元素继承了相同的默认命名空间节点root
。
XPath是XML文档的查询语言。因此,它只能帮助选择节点,但 XPath表达式的评估永远不会破坏,添加或更改节点,包括命名空间节点。
因此,作为评估XPath表达式的结果,不能销毁属于some_prefix:a
的默认命名空间节点 - 因此,当some_prefix:a
被序列化为文本时,将显示此命名空间节点。
解决方案:使用您最喜欢的承载XPath的PL来删除不需要的命名空间节点。
例如,如果托管语言是XSLT :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="default_ns">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates mode="delNS"
select="/*/d:subelement/*[1]"/>
</xsl:template>
<xsl:template match="*" mode="delNS">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="namespace::*[name()]"/>
<xsl:copy-of select="@*"/>
<xsl:apply-templates mode="delNS" select="node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
将此转换应用于提供的XML文档:
<root xmlns="default_ns">
<subelement>
<!-- here we can have an element of any namespace -->
<some_prefix:a xmlns:some_prefix="some_namespace">
<some_prefix:b/>
</some_prefix:a>
</subelement>
</root>
产生了想要的正确结果:
<some_prefix:a xmlns:some_prefix="some_namespace">
<some_prefix:b/>
</some_prefix:a>
答案 1 :(得分:0)
Dimitre 完全解释了为什么名称空间被继承以及如何使用XSLT去掉名称空间。
我使用了来自copy的 deepcopy 来删除不需要的命名空间。
这是我使用Python的最终解决方案:
from lxml import etree
from copy import deepcopy
xml = '''<root xmlns="default_ns">
<subelement>
<!-- here we can have an element of any namespace -->
<some_prefix:a xmlns:some_prefix="some_namespace">
<some_prefix:b/>
</some_prefix:a>
</subelement>
</root>'''
root = etree.fromstring(xml)
evaluator = etree.XPathEvaluator(root, namespaces={'def':'default_ns'})
child = evaluator.evaluate('//def:subelement/child::*')[0]
child = deepcopy(child)
a_string = etree.tostring(child)
print a_string