AddThis Social Bookmark Button

Print

Using Python and XML with Microsoft .NET My Services
Pages: 1, 2

Writing the Code

So how do you create this SOAP packet in Python? Fortunately, it’s pretty easy. There are several ways, but I chose to use the DOM support featured in PyXML. There are some imports and namespace definitions you need to create up-front, along with establishing your user identity and the target machine hosting .NET My Services. The need for a user ID will evolve with the use of .NET Passport for authentication. If you have a copy of .NET My Services beta, look in the introductory documentation to discover how to retrieve your own user ID.




"""
 hsclient.py
 A Python/XML client for .NET My Services Beta
"""
from xml.dom     import implementation
from xml.dom.ext import PrettyPrint
import httplib
import StringIO

puid = "3066"        # Your beta PUID
server = "skweetis"  # Server running .NET My Services

# define namespaces URIs
envns = "http://schemas.xmlsoap.org/soap/envelope/"
routex = "http://schemas.xmlsoap.org/rp/"
corehs = "http://schemas.microsoft.com/hs/2001/10/core"
secss = "http://schemas.xmlsoap.org/soap/security/2000-12"

Now that you’ve established some important definitions, you can go about creating the XML with the DOM. The DOM features methods for creating both simple elements and qualified namespace elements. For .NET My Services, you’ll use namespace qualified elements. Using PyXML, you create a document object using xml.dom.implementation’s createDocument method. You can then “stack” the other envelope components onto your new document by creating elements and appending them to the document root:


# create XML DOM document
doc = implementation.createDocument(None, '', None)

# create soap envelope element with namespaces 
soapenv = doc.createElementNS(envns, "Envelope")

# add soap envelope element
doc.appendChild(soapenv)

# create header element
header = doc.createElementNS(envns, "Header")
soapenv.appendChild(header)
path = doc.createElementNS(routex, "path")
header.appendChild(path)

After adding the path element to the header, you can go about adding the plethora of header information that was shown in the SOAP packet earlier. This includes much of the user and application identity information:


# add path elements
action = doc.createElementNS(routex, "action")
action.appendChild(
    doc.createTextNode(
        "http://schemas.microsoft.com/hs/2001/10/core#request"))
path.appendChild(action)

rev = doc.createElementNS(routex, "rev")
rev.appendChild(doc.createElementNS(routex, "via"))
path.appendChild(rev)

to = doc.createElementNS(routex, "to")
to.appendChild(doc.createTextNode("http://" + server))
path.appendChild(to)

id = doc.createElementNS(routex, "id")
id.appendChild(
  doc.createTextNode("C6B9D29A-D4A9-11D5-AD8F-00B0D0E9071D"))
path.appendChild(id)

# add license element
licenses = doc.createElementNS(secss, "licenses")
identity = doc.createElementNS(corehs, "identity")
identity.setAttribute("mustUnderstand", "1")
kerberos = doc.createElementNS(corehs, "kerberos")
kerberos.appendChild(doc.createTextNode("3066"))
identity.appendChild(kerberos)
licenses.appendChild(identity)
header.appendChild(licenses)

As you can see, when creating an XML document programmatically with the DOM, there is a lot of “stacking” involved. However, once completed, this way it’s easy to modify your packet dynamically and to add or remove elements and attributes. Working with a text blob of XML may seem easy at first, but when you need to start dynamically creating bits and pieces of it, you’ll find yourself juggling a mess of text strings together to try to create your XML.

Comment on this articleHave you used Python and XML with .NET? Share your experiences, and read what others have to say.
Post your comments

The SOAP body contains the query request, and you again use the DOM to build this part of the SOAP packet:


# add request element
request = doc.createElementNS(corehs, "request")
request.setAttribute("mustUnderstand", "1")
request.setAttribute("service", "myContacts")
request.setAttribute("document", "content")
request.setAttribute("method", "query")
request.setAttribute("genResponse", "always")
key = doc.createElementNS(corehs, "key")
key.setAttribute("instance", "1")
key.setAttribute("cluster", "1")
key.setAttribute("puid", puid)
request.appendChild(key)
header.appendChild(request)

# create body and queryRequest
body = doc.createElementNS(envns, "Body")
queryRequest = doc.createElementNS(corehs, "queryRequest")

# add a namespace attribute for the qname in our XPath
queryRequest.setAttribute("xmlns:mc",
    "http://schemas.microsoft.com/hs/2001/10/myContacts")
xpQuery = doc.createElementNS(corehs, "xpQuery")
xpQuery.setAttribute("select", "//mc:contact")

# finish the envelope
queryRequest.appendChild(xpQuery)
body.appendChild(queryRequest)
soapenv.appendChild(body)

At this point, your SOAP packet is finished. You could use PrettyPrint (from xml.dom.ext) to view your SOAP. In the next section, we’ll pour the SOAP into a string so that you can transport it using httplib.

Sending SOAP to .NET My Services

I chose to use httplib to send the SOAP packet, rather than a SOAP API, mainly to give a more detailed illustration of working with the DOM in Python (instead of using packet support of the API) and to more carefully illustrate how the XML is delivered to .NET My Services.

The HTTP headers used for .NET My Services are standard, and you use a POST method to deliver the XML:


soapString = StringIO.StringIO()
PrettyPrint(doc, soapString)

# send request to .NET My Services
http = httplib.HTTP(server)
http.putrequest("POST", "/myContacts")
http.putheader("User-Agent", "Simple")
http.putheader("Host", server)
http.putheader("Content-Length", "%d" % len(soapString.getvalue()))
http.putheader("Pragma", "no-cache")
http.endheaders()
http.send(soapString.getvalue())

# get the HTTP response
reply, message, headers = http.getreply()
print "Reply: ", reply, message
result = http.getfile().read()
print result

The only tricky part of posting your own SOAP packet is ensuring the content length is correctly specified. Of course, when retrieving the SOAP response, you would need to use a parser to determine the success of the request and to utilize the data returned. When the above code was run against my copy of .NET My Services, I received the following SOAP (and HTTP) response back from the server:


Reply:  200 OK
<?xml version='1.0'?>

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:hs="http://schemas.microsoft.com/hs/2001/10/core">
 <s:Header>
  <path xmlns="http://schemas.xmlsoap.org/rp/">
   <action>http://schemas.microsoft.com/hs/2001/10/core#response</action>
   <rev></rev>
   <from>http://skweetis</from>
   <id>89A1D531-E2AE-11D5-B853-00065B41A690</id>
   <relatesTo>C6B9D29A-D4A9-11D5-AD8F-00B0D0E9071D</relatesTo>
  </path>
  <hs:response>
  </hs:response>
 </s:Header>
 <s:Body>

<hs:queryResponse 
 xmlns:hs="http://schemas.microsoft.com/hs/2001/10/core" 
 xmlns:m="http://schemas.microsoft.com/hs/2001/10/myContacts" 
 xmlns:mp="http://schemas.microsoft.com/hs/2001/10/myProfile">
    <hs:xpQueryResponse status="success">
        <m:contact synchronize="no" 
           id="221C4B68-C22B-4247-ABEC-08CB40E8B434">
            <m:name id="63231675-ED1D-4937-AD82-2347A9B3A6B3">
                <mp:givenName xml:lang="en-us">John</mp:givenName>
                <mp:surname xml:lang="en-us">Doe</mp:surname>
            </m:name>
            <m:address id="BAD1BEF2-5ACB-453C-A443-9C30186F1C33">
                <hs:officialAddressLine 
                 xml:lang="en-us">One Microsoft Way</hs:officialAddressLine>
                <hs:primaryCity xml:lang="en-us">Redmond</hs:primaryCity>
                <hs:subdivision xml:lang="en-us">WA</hs:subdivision>
                <hs:postalCode>98052</hs:postalCode>
                <hs:countryCode>US</hs:countryCode>
            </m:address>
            <m:emailAddress id="CEAEA6DC-DAD3-483C-9296-D288DCF5D5A0">
                <mp:email>john.doe@microsoft.com</mp:email>
            </m:emailAddress>
        </m:contact>
        <m:contact synchronize="no" 
           id="25C91687-2A98-4A93-8168-A3DC0BC8828F">
            <m:name id="CCB1AC0C-E9FC-42C5-BAAF-36C81D3FF1FF">
                <mp:givenName xml:lang="en-us">Jane</mp:givenName>
                <mp:surname xml:lang="en-us">Doe</mp:surname>
            </m:name>
            <m:address id="85835630-4570-4F9C-B871-7995498BB349">
                <hs:officialAddressLine 
                 xml:lang="en-us">123 Anystreet</hs:officialAddressLine>
                <hs:primaryCity xml:lang="en-us">Woodinville</hs:primaryCity>
                <hs:subdivision xml:lang="en-us">WA</hs:subdivision>
                <hs:postalCode>98072</hs:postalCode>
                <hs:countryCode>US</hs:countryCode>
            </m:address>
            <m:emailAddress id="A7568B70-9037-4F89-9D75-FCC88DA7AE40">
                <mp:email>jane.doe@microsoft.com</mp:email>
            </m:emailAddress>
        </m:contact>
    </hs:xpQueryResponse>
</hs:queryResponse>
 </s:Body>
</s:Envelope>

As you can see, only two contact elements existed in my .NET Contacts service: one entry each for John and Jane Doe.

Looking Forward to more Python and XML

If you’re interested in learning more about Python and XML, O’Reilly’s upcoming Python & XML title is sure to please. The code presented here was written to work against the .NET My Services beta that was released at the Professional Developer’s Conference. Newer versions are sure to emerge periodically from Microsoft, and this code may need to be tweaked to support the new features as they become available.

Christopher A. Jones has an extensive background in Internet systems programming and XML.


O'Reilly & Associates will soon release (December 2001) Python & XML.

Return to the .NET DevCenter.