Как сказать lxml.etree.tostring (element) не писать пространства имен в python? - PullRequest
7 голосов
/ 10 августа 2011

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

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

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

Это то, что я нашел для 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 ]

4 голосов
/ 19 августа 2011

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

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

2 голосов
/ 08 августа 2012

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

from lxml import objectify
objectify.deannotate(root, cleanup_namespaces=True)
2 голосов
/ 10 августа 2011

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