0

I am trying to work with a restful interface which uses XML. I have narrowed my choice of XML library down to two.

lxml.objectify seems to be my preferred option based on the documentation but I am struggling.

Xmltodict seems to generate more typing and may be less pythonic but I seem to be able to make more headway.

I have an example I made with both. Xmltodict is working but with xml.objectify I can’t seem to figure out how to access/change the subelements for the credentials. My assumption is I could use dot notation to get down to the username and password but I have not been successful.

First, xmltodict code:

    from requests import post
import xmltodict
import pprint

pp=pprint.PrettyPrinter(indent=2)

req = '''<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
     <soap:Header>
          <HTNGHeader xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://htng.org/1.1/Header/">
               <From>
                    <Credential>
                         <password>PASSWORD</password>
                         <userName>UNAME</userName>
                    </Credential>
                    <systemId>2</systemId>
                </From>
                <To>
                     <systemId>2178</systemId>
                </To>
         </HTNGHeader>
     </soap:Header>
     <soap:Body />
</soap:Envelope>
'''

pwd = 'password'
uname = 'username'
url = 'https://url.com'

soap = xmltodict.parse(req)
soap['soap:Envelope']['soap:Header']['HTNGHeader']['From']['Credential']['password'] = pwd
soap['soap:Envelope']['soap:Header']['HTNGHeader']['From']['Credential']['userName'] = uname

soap_post = xmltodict.unparse(soap)

res = post(url, data=soap_post)

res_dict = xmltodict.parse(res.text)
pp.pprint(res_dict)

Now, lxml.objectify.

    from requests import post
from lxml import etree, objectify
import pprint

pp=pprint.PrettyPrinter(indent=2)

req = '''<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
     <soap:Header>
          <HTNGHeader xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://htng.org/1.1/Header/">
               <From>
                    <Credential>
                         <password>PASSWORD</password>
                         <userName>UNAME</userName>
                    </Credential>
                    <systemId>2</systemId>
                </From>
                <To>
                     <systemId>2178</systemId>
                </To>
         </HTNGHeader>
     </soap:Header>
     <soap:Body />
</soap:Envelope>
'''

request_doc = req.encode('utf-8')

pwd = 'password'
uname = 'username'
url = 'https://url.com

soap = objectify.fromstring(request_doc)

#here is my problem
soap.Header.HTNGHeader.From.Credential.userName = uname
soap.Header.HTNGHeader.From.Credential.password = pwd

soap_post = etree.tostring(soap)

url = 'https://connect.channelrush.net/reservationreceive/pickup?providerId=2'

res = post(url, data=soap_post)

res_obj = objectify.fromstring(res.text.encode('utf-8'))

pp.pprint(res.text)

With lxml.objectify I get the following exception:

File "f:\Dropbox\pms\channel_rush_interface_objectify.py", line 36, in <module>
  soap.Header.HTNGHeader.From.Credential.userName = uname
File "c:\Python34\Lib\site-packages\lxml\objectify.pyd", line 230, in lxml.objectify.ObjectifiedElement.__getattr__ (src\lxml\objectify.c:4443)
File "c:\Python34\Lib\site-packages\lxml\objectify.pyd", line 451, in lxml.objectify._lookupChildOrRaise (src\lxml\objectify.c:7228)

builtins.AttributeError: no such child: {http://schemas.xmlsoap.org/soap/envelope/}HTNGHeader

Can anyone point out how I can get lxml.objectify to work? Should I just stick to xmltodict?

Dkellygb
  • 866
  • 2
  • 8
  • 24

1 Answers1

0

From the lxml.objectify documentation:

During tag lookups, namespaces are handled mostly behind the scenes. If you access a child of an Element without specifying a namespace, the lookup will use the namespace of the parent. ... To access an element in a different namespace than its parent, you can use getattr() ... For convenience, there is also a quick way through item access. [examples removed]

In your case, try:

soap.Header["{http://htng.org/1.1/Header/}HTNGHeader"].From.Credential.userName = uname
soap.Header["{http://htng.org/1.1/Header/}HTNGHeader"].From.Credential.password = pwd
Robᵩ
  • 163,533
  • 20
  • 239
  • 308