How to insert namespace and prefixes into XML string using Python?
Suppose I have an XML string:
<A>
<B foo="123">
<C>thing</C>
<D>stuff</D>
</B>
</A>
and I want to insert a namespace of the type used in the XML schema by putting a prefix in front of all element names.
<A xmlns:ns1="www.example.com">
<ns1:B foo="123">
<ns1:C>thing</ns1:C>
<ns1:D>stuff</ns1:D>
</ns1:B>
</A>
Is there a way to do this (other than brute force find-replace or regex) using lxml.etree
or a similar library?
source to share
I don't think it can only be done with ElementTree.
Manipulating namespaces is sometimes surprisingly difficult. There are many questions about this. Even with the more advanced lxml library it can be very tricky. See the following questions:
- lxml: add namespace for file input
- Change namespaces in a given XML document using lxml
- lxml etree xmlparser removes unwanted namespace
Below is a solution using XSLT.
code:
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)
Output:
<A xmlns:ns1="www.example.com">
<ns1:B foo="123">
<ns1:C>thing</ns1:C>
<ns1:D>stuff</ns1:D>
</ns1:B>
</A>
source to share
Use ET.register_namespace('ns1', 'www.example.com')
to register namespaces with ElementTree. This is required, so it write()
uses the registered prefix. (I have some code that uses a prefix ''
(empty string) for the default namespace)
Then prefix each element name {www.example.com}
. For example: root.find('{www.example.com}B')
.
source to share