Как вставить пространство имен и префиксы в строку XML с Python?

Предположим, у меня есть строка XML:

<A>
    <B foo="123">
        <C>thing</C>
        <D>stuff</D>
    </B>
</A>

и я хочу вставить пространство имен типа, используемого схемой XML, поместив префикс перед всеми именами элементов.

<A xmlns:ns1="www.example.com">
    <ns1:B foo="123">
        <ns1:C>thing</ns1:C>
        <ns1:D>stuff</ns1:D>
    </ns1:B>
</A>

Есть ли способ сделать это (кроме грубой силы find-replace или regex), используя lxml.etree или похожая библиотека?

2 ответа

Я не думаю, что это может быть сделано только с ElementTree.

Манипулирование пространствами имен иногда удивительно сложно. Есть много вопросов об этом здесь, на SO. Даже с более продвинутой библиотекой lxml это может быть очень сложно. Смотрите эти связанные вопросы:

Ниже приведено решение, которое использует XSLT.

Код:

from lxml import etree

XML = '''
<A>
    <B foo="123">
        <C>thing</C>
        <D>stuff</D>
    </B>
</A>'''

XSLT = '''
<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ns1="www.example.com">
 <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

  <xsl:template match="*">
   <xsl:element name="ns1:{name()}">
    <xsl:apply-templates select="node()|@*"/>
   </xsl:element>
  </xsl:template>

  <!-- No prefix on the A element -->
  <xsl:template match="A">
   <A xmlns:ns1="www.example.com">
    <xsl:apply-templates select="node()|@*"/>
   </A>
  </xsl:template>
</xsl:stylesheet>'''

xml_doc = etree.fromstring(XML)
xslt_doc = etree.fromstring(XSLT)
transform = etree.XSLT(xslt_doc)
print transform(xml_doc)

Выход:

<A xmlns:ns1="www.example.com">
    <ns1:B foo="123">
        <ns1:C>thing</ns1:C>
        <ns1:D>stuff</ns1:D>
    </ns1:B>
</A>

Использование ET.register_namespace('ns1', 'www.example.com') зарегистрировать пространство имен с ElementTree. Это нужно так write() использует зарегистрированный префикс. (У меня есть код, который использует префикс '' (пустая строка) для пространства имен по умолчанию)

Затем префикс каждого имени элемента {www.example.com}, Например: root.find('{www.example.com}B'),

import xml.etree.ElementTree as ET

name_space = {
# namespace defined below
"xmlns:ns1":"www.example.com""="www.example.com"
}

A = ET.Element('A', name_space)
B = ET.SubElement(A, 'ns1:B')
C = ET.SubElement(B, 'ns1:C')
C.text = 'thing'

Вы можете передать пространства имен конструктору Element, в этот момент вы можете ссылаться на любой дочерний компонент. обратите внимание, вы можете определить более одного. это решение сработало для меня.

Другие вопросы по тегам