【问题标题】:changing element namespace in lxml更改 lxml 中的元素命名空间
【发布时间】:2018-08-02 13:38:56
【问题描述】:

对于lxml,我不确定如何正确删除现有元素的命名空间并设置一个新元素。

例如,我正在解析这个最小的 xml 文件:

<myroot xmlns="http://myxml.com/somevalue">
    <child1>blabla</child1>
    <child2>blablabla</child2>
</myroot>

...我希望它变成:

<myroot xmlns="http://myxml.com/newvalue">
    <child1>blabla/child1>
    <child2>blablabla</child2>
</myroot>

lxml:

from lxml import etree as ET
tree = ET.parse('myfile.xml')
root= tree.getroot()

如果我检查root

In [7]: root
Out[7]: <Element {http://myxml.com/somevalue}myroot at 0x7f6e13832588>
In [8]: root.nsmap
Out[8]: {None: 'http://myxml.com/somevalue'}
In [11]: root.tag
Out[11]: '{http://myxml.com/somevalue}myroot'

理想情况下,我希望得到:

In [8]: root.nsmap
Out[8]: {None: 'http://myxml.com/newvalue'}
In [11]: root.tag
Out[11]: '{http://myxml.com/newvalue}myroot'

至于标签,只需设置正确的字符串即可。 nsmap呢?

【问题讨论】:

  • 查看我的这个答案:stackoverflow.com/a/20956523/407651。它的得分为 -2,但它提供了我认为更改命名空间的最简单方法。
  • 这是一个简单案例的解决方法,但恐怕它没有提供问题的答案
  • 是的,这是一种解决方法。不幸的是,我不知道有什么更好的。操作命名空间可能非常困难。更新nsmap 无效。请参阅bugs.launchpad.net/lxml/+bug/555602(在链接答案的评论中提到了这个问题)。另见stackoverflow.com/a/31870245/407651
  • 我明白了。似乎不可思议的是,Python 的标准库中没有这么简单的东西......在xml.etree.ElementTree 中,我可以通过从标记值中删除{*} 来删除所有命名空间,然后在所需元素上使用.set('xmlns', 'someURI') 重置它们。使用lxml,会产生带有两个xmlns 标签的元素:原始标签和新标签。我很失望......
  • @mzjn ... 否决票可能是由于将 XML 视为文本文件而不使用正确的 DOM 库方法。

标签: python xml lxml xml-namespaces elementtree


【解决方案1】:

我同意 mzjn 和 Parfait;我会使用 XSLT 来更改命名空间。

通过将新旧命名空间作为参数传入,您可以使 XSLT 相当可重用。

示例...

XML 输入 (input.xml)

<myroot xmlns="http://myxml.com/somevalue">
    <child1>blabla</child1>
    <child2>blablabla</child2>
</myroot>

XSLT 1.0 (test.xsl)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="orig_namespace"/>
  <xsl:param name="new_namespace"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*" priority="1">
    <xsl:choose>
      <xsl:when test="namespace-uri()=$orig_namespace">
        <xsl:element name="{name()}" namespace="{$new_namespace}">
          <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

Python

from lxml import etree

tree = etree.parse("input.xml")
xslt = etree.parse("test.xsl")

orig_namespace = "http://myxml.com/somevalue"
new_namespace = "http://myxml.com/newvalue"

new_tree = tree.xslt(xslt, orig_namespace=f"'{orig_namespace}'",
                     new_namespace=f"'{new_namespace}'")
print(etree.tostring(new_tree, pretty_print=True).decode("utf-8"))

输出

<myroot xmlns="http://myxml.com/newvalue">
  <child1>blabla</child1>
  <child2>blablabla</child2>
</myroot>

此外,如果您使用以下输入(使用命名空间前缀)...

<ns1:myroot xmlns:ns1="http://myxml.com/somevalue">
    <ns1:child1>blabla</ns1:child1>
    <ns1:child2>blablabla</ns1:child2>
</ns1:myroot>

你得到这个输出...

<ns1:myroot xmlns:ns1="http://myxml.com/newvalue">
  <ns1:child1>blabla</ns1:child1>
  <ns1:child2>blablabla</ns1:child2>
</ns1:myroot>

有关将 XSLT 与 lxml 结合使用的更多信息,请参阅 https://lxml.de/xpathxslt.html

【讨论】:

  • 此代码不起作用。 First the transform command needs to be defined,例如transform = etree.XSLT(xslt),然后将其应用于 XML 文档:newtree = transform(tree, orig_namespace=...)。我尝试更新您的答案,但被拒绝...
  • @ganzpopp - 嗯...如果它不起作用,我想我不会发布它。我总是在发布之前进行测试。运行时是否出现错误? python和lxml是什么版本的?
  • 专门检查了您的代码,不适用于 Python 3.7.5 和 lxml 4.4.1。
  • @ganzpopp - 我还没有机会再试一次,但你能解释一下“不起作用”是什么意思吗?有没有具体的错误?另外,我可能使用的是 Python 3.6;你能试试那个版本吗?我会在接下来的 24 小时内尝试这两个版本。
  • 我可以确认该代码适用于 Python 3.7.1 和 lxml 4.4.1(我很久以前就投票了!)。
猜你喜欢
  • 2011-07-26
  • 1970-01-01
  • 1970-01-01
  • 2020-10-05
  • 2018-06-03
  • 2012-06-21
  • 2011-04-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多