Как сказать lxml.этри.tostring (элемент) не писать пространства имен в python?


У меня есть огромный xml-файл (1 гигабайт). Я хочу переместить некоторые элементы (entrys) в другой файл с тем же заголовком и спецификациями.

Предположим, что исходный файл содержит эту запись с тегом <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.этри.iterparse для итерации по файлу. Работать отлично. Когда я нахожу элемент с тегом <to_move>, предположим, что он хранится в переменной element I do

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>

Итак, вопрос в том, как сказать этри.tostring () не писать xmlns:="some". Возможно ли это? Я боролся с api-документацией lxml.этри, но я не мог найти удовлетворительного ответа.

Вот что я нашел для 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() не помогает. Какие-нибудь предложения или поправки?
3 6

3 ответа:

Я часто хватаю пространство имен, чтобы сделать для него псевдоним следующим образом:

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. Но не проходите мимо первого >.

Это больше в комментарии к ответу "unutbu", в котором было предложено очистить пространство имен без приведения примера. это может быть то, что вы ищете...

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

Есть способ удалить пространства имен с помощью 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>