I'm using Python 3.2's xml.etree.ElementTree, and am attempting to generate XML like this:
<XnaContent xmlns:data="Model.Data">
<Asset Type="data:MyData">
...
The format is out of my control (it's XNA). Notice that the data XML namespace is never actually used to qualify elements or attributes, but rather to qualify attribute values to XNA. My code looks like this:
root = Element('XnaContent')
ET.register_namespace('data', 'Model.Data')
asset = SubElement(root, 'Asset', {"Type": "data:MyData"})
However, the output looks like (pretty-printed by me):
<XnaContent>
<Asset Type="data:MyData">
...
</Asset>
</XnaContent>
How can I get the data XML namespace included in the output?
>>>print ET.tostring(doc, pretty_print=True)
<XnaContent>
<Asset Type="data:MyData"/>
<Asset Type="data:MyData"/>
</XnaContent>
>>> tree=ET.ElementTree(doc)
>>> root=tree.getroot()
>>> nsmap=root.nsmap
>>> nsmap['data']="ModelData"
>>> new_root = ET.Element(root.tag, nsmap=nsmap)
>>> print ET.tostring(new_root, pretty_print=True)
<XnaContent xmlns:data="ModelData"/>
>>> new_root[:] = root[:]
>>> print ET.tostring(new_root, pretty_print=True)
<XnaContent xmlns:data="ModelData">
<Asset Type="data:MyData"/>
<Asset Type="data:MyData"/>
</XnaContent>
import xml.etree.ElementTree as ET
content = '''
<XnaContent>
<Asset Type="data:MyData"/>
<Asset Type="data:MyData"/>
</XnaContent>'''
doc = ET.fromstring(content)
ET.register_namespace('data','ModelData')
tree = ET.ElementTree(doc)
root = tree.getroot()
root.tag = '{ModelData}XnaContent'
print(ET.tostring(root, method = 'xml'))
yields
<data:XnaContent xmlns:data="ModelData">
<Asset Type="data:MyData" />
<Asset Type="data:MyData" />
</data:XnaContent>
Related
I have an issue while creating an XML-file for a SOAP Call
I am using the following code to create the XML:
from lxml import etree as ET
SOAP_NS = "URL"
ENCODE_NS = "URL2/soap-encoding"
ns_map = {'soap' : SOAP_NS, 'encodingStyle' : ENCODE_NS}
root = ET.Element(ET.QName(SOAP_NS, 'Envelope'), nsmap=ns_map)
body = ET.SubElement(root, ET.QName(SOAP_NS, 'Body'), nsmap=ns_map)
Data = ET.SubElement(body, 'Data')
Data.text="1234"
Data.set('type','import')
xml_file = ET.ElementTree(root)
xml_file.write('Test.xml', pretty_print=True)
Thus I get the following XML-file:
<soap:Envelope xmlns:soap="URL1" xmlns:encodingStyle="URL2/soap-encoding">
<soap:Body>
<Data type="import">1234</Data>
</soap:Body>
</soap:Envelope>
The first line of the XML-file I need to create has to be like that
<soap:Envelope xmlns:soap="URL1" soap:encodingStyle="URL2/soap-encoding">
<soap:Body>
<Data type="import">1234</Data>
</soap:Body>
</soap:Envelope>
How do I change the prefix/namespace of URL 2 from xmlns:encodingStyle to soap:encodingStyle or if my approach is wrong how do I add soap:encodingStyle to the envolope?
Thanks in advance
soap:encodingStyle is an attribute bound to a namespace. Add it using the set() method.
from lxml import etree as ET
SOAP_NS = "URL"
ENCODE_NS = "URL2/soap-encoding"
ns_map = {'soap' : SOAP_NS}
root = ET.Element(ET.QName(SOAP_NS, 'Envelope'), nsmap=ns_map)
root.set(ET.QName(SOAP_NS, "encodingStyle"), ENCODE_NS)
body = ET.SubElement(root, ET.QName(SOAP_NS, 'Body'), nsmap=ns_map)
Data = ET.SubElement(body, 'Data')
Data.text="1234"
Data.set('type','import')
xml_file = ET.ElementTree(root)
xml_file.write('Test.xml', pretty_print=True)
I cant get my mind around this nor working properly:
data='''<?xml version="1.0" encoding="UTF-8"?>\n<div type="docs" xml:base="/kime-api/prod/api/emi/2" xml:lang="ja" xml:id="39532e30"> <div n="0001" type="doc" xml:id="_5738d00002"></div></div>'''
parser = etree.XMLParser(resolve_entities=False, strip_cdata=False, recover=True, ns_clean=True)
# I tried with and without this following line
#data = data.replace('<?xml version="1.0" encoding="UTF-8"?>','')
XML_tree = etree.fromstring(data.encode() , parser=parser)
lang = XML_tree.xpath('.//div[#xml:lang]')
lang
lang is an empty list and there is ONE element like: xml:lang="ja" in the XML.
What am I doing wrong please?
You could just do xpath(#xml:lang).
XML_tree = etree.fromstring(data.encode() , parser=parser)
lang = XML_tree.xpath('#xml:lang')
print(lang)
Output:
['ja']
XML_tree represents the root element (the <div> with an xml:lang attribute).
If you want to get the language, use the following:
lang = XML_tree.xpath('#xml:lang')
Hello I am making a requests call to return order data from a online store. My issue is that once I have passed my data to a root variable the method iter is not returning the correct results. e.g. Display multiple tags of the same name rather than one and not showing the data within the tag.
I thought this was due to the XML not being correctly formatted so I formatted it by saving it to a file using pretty_print but that hasn't fixed the error.
How do I fix this? - Thanks in advance
Code:
import requests, xml.etree.ElementTree as ET, lxml.etree as etree
url="http://publicapi.ekmpowershop24.com/v1.1/publicapi.asmx"
headers = {'content-type': 'application/soap+xml'}
body = """<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetOrders xmlns="http://publicapi.ekmpowershop.com/">
<GetOrdersRequest>
<APIKey>my_api_key</APIKey>
<FromDate>01/07/2018</FromDate>
<ToDate>04/07/2018</ToDate>
</GetOrdersRequest>
</GetOrders>
</soap12:Body>
</soap12:Envelope>"""
#send request to ekm
r = requests.post(url,data=body,headers=headers)
#save output to file
file = open("C:/Users/Mark/Desktop/test.xml", "w")
file.write(r.text)
file.close()
#take the file and format the xml
x = etree.parse("C:/Users/Mark/Desktop/test.xml")
newString = etree.tostring(x, pretty_print=True)
file = open("C:/Users/Mark/Desktop/test.xml", "w")
file.write(newString.decode('utf-8'))
file.close()
#parse the file to get the roots
tree = ET.parse("C:/Users/Mark/Desktop/test.xml")
root = tree.getroot()
#access elements names in the data
for child in root.iter('*'):
print(child.tag)
#show orders elements attributes
tree = ET.parse("C:/Users/Mark/Desktop/test.xml")
root = tree.getroot()
for order in root.iter('{http://publicapi.ekmpowershop.com/}Order'):
out = {}
for child in order:
if child.tag in ('OrderID'):
out[child.tag] = child.text
print(out)
Elements output:
{http://publicapi.ekmpowershop.com/}Orders
{http://publicapi.ekmpowershop.com/}Order
{http://publicapi.ekmpowershop.com/}OrderID
{http://publicapi.ekmpowershop.com/}OrderNumber
{http://publicapi.ekmpowershop.com/}CustomerID
{http://publicapi.ekmpowershop.com/}CustomerUserID
{http://publicapi.ekmpowershop.com/}Order
{http://publicapi.ekmpowershop.com/}OrderID
{http://publicapi.ekmpowershop.com/}OrderNumber
{http://publicapi.ekmpowershop.com/}CustomerID
{http://publicapi.ekmpowershop.com/}CustomerUserID
Orders Output:
{http://publicapi.ekmpowershop.com/}Order {}
{http://publicapi.ekmpowershop.com/}Order {}
XML Structure after formating:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetOrdersResponse xmlns="http://publicapi.ekmpowershop.com/">
<GetOrdersResult>
<Status>Success</Status>
<Errors/>
<Date>2018-07-10T13:47:00.1682029+01:00</Date>
<TotalOrders>10</TotalOrders>
<TotalCost>100</TotalCost>
<Orders>
<Order>
<OrderID>100</OrderID>
<OrderNumber>102/040718/67</OrderNumber>
<CustomerID>6910</CustomerID>
<CustomerUserID>204</CustomerUserID>
<FirstName>TestFirst</FirstName>
<LastName>TestLast</LastName>
<CompanyName>Test Company</CompanyName>
<EmailAddress>test#Test.com</EmailAddress>
<OrderStatus>Dispatched</OrderStatus>
<OrderStatusColour>#00CC00</OrderStatusColour>
<TotalCost>85.8</TotalCost>
<OrderDate>10/07/2018 14:30:43</OrderDate>
<OrderDateISO>2018-07-10T14:30:43</OrderDateISO>
<AbandonedOrder>false</AbandonedOrder>
<EkmStatus>SUCCESS</EkmStatus>
</Order>
</Orders>
<Currency>GBP</Currency>
</GetOrdersResult>
</GetOrdersResponse>
</soap:Body>
</soap:Envelope>
You need to consider the namespace when checking for tags.
>>> # Include the namespace part of the tag in the tag values that we check.
>>> tags = ('{http://publicapi.ekmpowershop.com/}OrderID', '{http://publicapi.ekmpowershop.com/}OrderNumber')
>>> for order in root.iter('{http://publicapi.ekmpowershop.com/}Order'):
... out = {}
... for child in order:
... if child.tag in tags:
... out[child.tag] = child.text
... print(out)
...
{'{http://publicapi.ekmpowershop.com/}OrderID': '100', '{http://publicapi.ekmpowershop.com/}OrderNumber': '102/040718/67'}
If you don't want the namespace prefixes in the output, you can strip them by only including that part of the tag after the } character.
>>> for order in root.iter('{http://publicapi.ekmpowershop.com/}Order'):
... out = {}
... for child in order:
... if child.tag in tags:
... out[child.tag[child.tag.index('}')+1:]] = child.text
... print(out)
...
{'OrderID': '100', 'OrderNumber': '102/040718/67'}
I would like to add attribute to a lxml Element like this
<outer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
<field1 name="blah">some value1</field1>
<field2 name="asdfasd">some value2</field2>
</Header>
</outer>
Here is what I have
E = lxml.builder.ElementMaker()
outer = E.outer
header = E.Header
FIELD1 = E.field1
FIELD2 = E.field2
the_doc = outer(
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance",
XML_2_HEADER(
FIELD1('some value1', name='blah'),
FIELD2('some value2', name='asdfasd'),
),
)
seems like this line is causing some problem
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance",
even if I replace it with
'xmlns:xsi'="http://www.w3.org/2001/XMLSchema-instance",
it won't work.
What is a way to add attribute to lxml Element?
That's a namespace definition, not an ordinary XML attribute. You can pass namespace information to ElementMaker() as a dictionary, for example :
from lxml import etree as ET
import lxml.builder
nsdef = {'xsi':'http://www.w3.org/2001/XMLSchema-instance'}
E = lxml.builder.ElementMaker(nsmap=nsdef)
doc = E.outer(
E.Header(
E.field1('some value1', name='blah'),
E.field2('some value2', name='asdfasd'),
),
)
print ET.tostring(doc, pretty_print=True)
output :
<outer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
<field1 name="blah">some value1</field1>
<field2 name="asdfasd">some value2</field2>
</Header>
</outer>
Link to the docs: http://lxml.de/api/lxml.builder.ElementMaker-class.html
i have xml file like
<data>
<person>
<Name>xyz</Name>
<add>abc</add>
</person>
</data>
i want to add another person node like
<data>
<person>
<Name>xyz</Name>
<add>abc</add>
</person>
<person>
<Name>def</Name>
</person>
</data>
my current python code is
import xml.etree.ElementTree as ET
from xml.etree.ElementTree import Element
from xml.etree.ElementTree import ElementTree
root = ET.parse("Lexicon.xml").getroot()
creRoot = Element("person")
creDictionary = Element("Name")
creDictionary.text = "def"
creRoot.append(creDictionary)
print(ET.tostring(creRoot))
creTree= ElementTree(creRoot)
creTree.write("Lexicon.xml")
when i run this code it will create xml file rather then add and the result is
<person>
<Name>def</Name>
</person>
and it will remove all previous data..
Kindly anyone who can solve it.. Thanks in advance
SubElement shall be used to add nodes to existing node:
import xml.etree.ElementTree as etree
data = etree.XML(input)
person = etree.SubElement(data, 'person')
name = etree.SubElement(person, 'Name')
name.text = 'def'
print(etree.tostring(data))
We need to append new create element to respective parent element.
Demo:
>>> import xml.etree.ElementTree as ET
>>> input_data = """<data>
... <person>
... <Name>xyz</Name>
... <add>abc</add>
... </person>
... </data>"""
#- Create new Element.
>>> person_tag = ET.Element("person")
>>> name_tag = ET.Element("Name")
#- Add text to Element.
>>> name_tag.text = "def"
#- Append Element to Parent Element.
>>> person_tag.append(name_tag)
>>>
#- Just print Parent Element
>>> ET.tostring(person_tag)
'<person><Name>def</Name></person>'
>>>
>>>
#- Created ET object by formstring.
>>> root = ET.fromstring(input_data)
>>>
#- Append above element to root element
>>> root.append(person_tag)
#- Print root Element.
>>> print ET.tostring(root)
<data>
<person>
<Name>xyz</Name>
<add>abc</add>
</person>
<person><Name>def</Name></person></data>
>>> print ET.tostring(root, method="xml")
<data>
<person>
<Name>xyz</Name>
<add>abc</add>
</person>
<person><Name>def</Name></person></data>
>>>
Note: Best to use lxml b