【问题标题】:How to tell lxml.etree.tostring(element) not to write namespaces in python?如何告诉 lxml.etree.tostring(element) 不要在 python 中编写命名空间?
【发布时间】:2011-10-23 16:05:04
【问题描述】:

我有一个巨大的 xml 文件 (1 Gig)。我想将一些元素(条目)移动到具有相同标题和规范的另一个文件中。

假设原始文件包含带有标签<to_move>的条目:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE some SYSTEM "some.dtd">
<some>
...
<to_move date="somedate">
    <child>some text</child>
    ...
...
</to_move>
...
</some>

我使用 lxml.etree.iterparse 来遍历文件。工作正常。当我找到带有标签&lt;to_move&gt; 的元素时,假设它存储在变量element 中,我这样做了

new_file.write(etree.tostring(element))

但这会导致

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE some SYSTEM "some.dtd">
<some>
...
<to_move xmlns:="some" date="somedate">  # <---- Here is the problem. I don't want the namespace.
    <child>some text</child>
    ...
...
</to_move>
...
</some>

所以问题是:如何告诉 etree.tostring() 不要写 xmlns:="some"。这可能吗?我在 lxml.etree 的 api-documentation 中苦苦挣扎,但找不到令人满意的答案。

这是我为etree.trostring找到的:

tostring(element_or_tree, encoding=None, method="xml",
xml_declaration=None, pretty_print=False, with_tail=True,
standalone=None, doctype=None, exclusive=False, with_comments=True)

将元素序列化为其 XML 的编码字符串表示 树。

对我来说,tostring() 的每个参数似乎都没有帮助。有什么建议或更正吗?

【问题讨论】:

    标签: python namespaces lxml tostring elementtree


    【解决方案1】:

    我经常像这样抓取一个命名空间为它创建一个别名:

    someXML = lxml.etree.XML(someString)
    if ns is None:
          ns = {"m": someXML.tag.split("}")[0][1:]}
    someid = someXML.xpath('.//m:ImportantThing//m:ID', namespaces=ns)
    

    您可以执行类似的操作来获取命名空间,以创建一个在使用 tostring 后将其清理的正则表达式。

    或者你可以清理输入字符串。找到第一个空格,检查后面是否有xmlns,如果是,则删除整个xmlns直到下一个空格,如果没有则删除空格。重复直到没有更多的空格或 xmlns 声明。但不要超过第一个&gt;

    【讨论】:

      【解决方案2】:

      这更多是对“unutbu”答案的评论,其中需要清理命名空间的建议,但没有给出示例。这可能就是您正在寻找的...

      from lxml import objectify
      objectify.deannotate(root, cleanup_namespaces=True)
      

      【讨论】:

      • 这不会删除任何命名空间。来自文档:“通过删除 'py:pytype' 和/或 'xsi:type' 属性和/或 'xsi:nil' 属性来递归地对 XML 树的元素进行反注释。”
      • 这几乎对我有用,我在结果字符串中仍然有一个xmlns 属性,有没有办法在不使用正则表达式的情况下摆脱它?
      • 这行得通。此外,使用objectify 和参数annotate=False 生成元素也可以:例如:myE = objectify.ElementMaker(annotate=False) 然后x = myE.rootElem();您可以使用etree.tostring(x, method='xml') 对其进行序列化,然后只返回&lt;rootElem/&gt;
      【解决方案3】:

      有办法remove namespaces with XSLT:

      import io
      import lxml.etree as ET
      
      
      def remove_namespaces(doc):
          # http://wiki.tei-c.org/index.php/Remove-Namespaces.xsl
          xslt='''<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
          <xsl:output method="xml" indent="no"/>
      
          <xsl:template match="/|comment()|processing-instruction()">
              <xsl:copy>
                <xsl:apply-templates/>
              </xsl:copy>
          </xsl:template>
      
          <xsl:template match="*">
              <xsl:element name="{local-name()}">
                <xsl:apply-templates select="@*|node()"/>
              </xsl:element>
          </xsl:template>
      
          <xsl:template match="@*">
              <xsl:attribute name="{local-name()}">
                <xsl:value-of select="."/>
              </xsl:attribute>
          </xsl:template>
          </xsl:stylesheet>
          '''
      
          xslt_doc = ET.parse(io.BytesIO(xslt))
          transform = ET.XSLT(xslt_doc)
          doc = transform(doc)
          return doc
      
      doc = ET.parse('data.xml')
      doc = remove_namespaces(doc)
      print(ET.tostring(doc))
      

      产量

      <some>
      
      <to_move date="somedate">
          <child>some text</child>
      </to_move>
      
      </some>
      

      【讨论】:

      • 我明白,命名空间背后的好主意。 :-) 但是在我的这种特殊情况下,它根本没有利润。这并不意味着我会永远忽略它们。 :-) 谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多