Как вставить пространство имен и префиксы в строку 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 это может быть очень сложно. Смотрите эти связанные вопросы:
- lxml: добавить пространство имен во входной файл
- Изменить пространства имен в данном документе XML с помощью lxml
- lxml etree xmlparser удаляет нежелательное пространство имен
Ниже приведено решение, которое использует 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, в этот момент вы можете ссылаться на любой дочерний компонент. обратите внимание, вы можете определить более одного. это решение сработало для меня.