<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Applied Miscellany &#187; SOAP</title>
	<atom:link href="http://www.appliedmiscellany.com/blog/archives/category/programming/soap/feed" rel="self" type="application/rss+xml" />
	<link>http://www.appliedmiscellany.com/blog</link>
	<description>Technology, Tech Policy, Internet, Gadgets, Software, ...</description>
	<lastBuildDate>Tue, 15 May 2007 12:07:19 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Python / SOAP: A First Encounter</title>
		<link>http://www.appliedmiscellany.com/blog/archives/10</link>
		<comments>http://www.appliedmiscellany.com/blog/archives/10#comments</comments>
		<pubDate>Thu, 09 Feb 2006 22:23:27 +0000</pubDate>
		<dc:creator>Scott Karlin</dc:creator>
				<category><![CDATA[All]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[SOAP]]></category>

		<guid isPermaLink="false">http://www.appliedmiscellany.com/blog/archives/10</guid>
		<description><![CDATA[This entry describes my recent foray into creating a Simple Object Access Protocol (SOAP) client. Perhaps some will find the information useful and others can provide some helpful hints in the comments.
The web service I wanted to access exports a method that returns an array of strings each with a particular structure (this will be [...]]]></description>
			<content:encoded><![CDATA[<p>This entry describes my recent foray into creating a Simple Object Access Protocol (SOAP) client. Perhaps some will find the information useful and others can provide some helpful hints in the comments.</p>
<p>The web service I wanted to access exports a method that returns an array of strings each with a particular structure (this will be important later). The details of this service are not important so let&#8217;s call the method <code>getList</code>. Since this client would be run from a Linux command line and I wanted to brush-up on my Python programming skills, I went to work looking for an appropriate SOAP library for Python. A search on Google led me to two primary choices, SOAPpy and ZSI, both part of the <a href="http://pywebsvcs.sourceforge.net/">Python Web Services</a> project.  SOAPpy looked to be the easier to use and so I installed SOAPpy version 0.12.0 and started with this bit of code:</p>
<pre>from SOAPpy import WSDL
server = WSDL.Proxy("http://blah.blah.blah/blah?wsdl")

server.soapproxy.config.dumpSOAPOut = 1
server.soapproxy.config.dumpSOAPIn = 1

result = server.getList("param1", "param2")

# Process result...</pre>
<p>From everything I read, this should have worked.  Unfortunately, I was greeted with a &#8220;duplicate attribute&#8221; exception from the depths of the <a href="http://docs.python.org/lib/module-xml.sax.html">xml.sax</a> parser:</p>
<pre class="wide">Traceback (most recent call last):
  File "soaptest.py", line 7, in ?
    result = server.getList("param1", "param2")
  File "/usr/lib/python2.4/site-packages/SOAPpy/Client.py",
  line 470, in __call__
    return self.__r_call(*args, **kw)
  File "/usr/lib/python2.4/site-packages/SOAPpy/Client.py",
  line 492, in __r_call
    self.__hd, self.__ma)
  File "/usr/lib/python2.4/site-packages/SOAPpy/Client.py",
  line 395, in __call
    p, attrs = parseSOAPRPC(r, attrs = 1)
  File "/usr/lib/python2.4/site-packages/SOAPpy/Parser.py",
  line 1049, in parseSOAPRPC
    t = _parseSOAP(xml_str, rules = rules)
  File "/usr/lib/python2.4/site-packages/SOAPpy/Parser.py",
  line 1032, in _parseSOAP
    raise e
xml.sax._exceptions.SAXParseException: :1:514: duplicate attribute</pre>
<p>Armed with this traceback and the fact that the WDSL file (when viewed with a browser) indicated that the server is &#8220;Apache Axis version 1.2.1,&#8221; I searched the web and found these bug reports:</p>
<ul>
<li><a href="http://issues.apache.org/jira/browse/AXIS-2127">http://issues.apache.org/jira/browse/AXIS-2127</a></li>
<li><a href="http://jira.atlassian.com/browse/JRA-7321">http://jira.atlassian.com/browse/JRA-7321</a></li>
</ul>
<p>With the SOAP debugging turned on, I could see that the server was returning the expected list of items&#8212;the XML parser was simply preventing me from getting at them.  As it turned out, there was indeed a duplicated <code>xsi:type="soapenc:Array"</code> attribute in the returned SOAP message.  The bug reports above imply that this issue is fixed in Axis 1.3; however, since the particular Axis SOAP servlet I needed to use was part of a bundled system (that I didn&#8217;t have control over), upgrading Axis was not an option.</p>
<h3>The Workaround</h3>
<p>The workaround (actually a &#8220;hack&#8221;) that I used was to temporarily redirect the SOAP debugging output sent to <code>stdout</code> to an internal buffer.  From there, I could leverage the fact that the returned strings had a nice structure to extract them from the buffer using a regular expression:</p>
<pre>from SOAPpy import WSDL
import xml.sax
import sys
import re

class Sniff:
   def __init__(self):
      self.b = ""

   def write(self, s):
      self.b = self.b + s

   def flush(self):
      pass

   def buffer(self):
      return self.b

server = WSDL.Proxy("http://blah.blah.blah/blah?wsdl")

server.soapproxy.config.dumpSOAPOut = 1
server.soapproxy.config.dumpSOAPIn = 1

sniff      = Sniff()
sys.stdout = sniff
try:
   result = server.getList("param1", "param2")
except xml.sax.SAXParseException, e:
   pass
sys.stdout = sys.__stdout__

c = re.compile(r">([a-z0-9]+):([^,:&lt; ]+),([^:&lt;]+):([^&lt;]+)&lt;")
result = re.findall(c, sniff.buffer())

# Process result...</pre>
<p>I should mention that before I resorted to this hack, I did try using ZSI and even <a href="http://csoap.sourceforge.net/">cSOAP</a> (a C library for SOAP).  ZSI gave a richer set of controls over the SOAP protocol; however, I found that with so many knobs to fiddle with, I could only elicit from the server a <code>500 Internal Server Error</code> response at the HTTP layer with a corresponding <code>Server.userException</code> response at the SOAP layer.  Even if I could get the outgoing SOAP message to be accepted by the server, I wasn&#8217;t sure if I wouldn&#8217;t have the same parsing problem as I did with the SOAPpy implementation.  With cSOAP, I went straight to my C programming roots.  Unfortunately, the installation documentation was a little sparse and I didn&#8217;t pursue this very far before the above hack came to mind.</p>
<p>Of course, I would rather not have had to resort to the workaround.  My current implementation is more brittle than it should be.  For a web service this simple, I would have preferred <a href="http://www.xmlrpc.com/">XML-RPC</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.appliedmiscellany.com/blog/archives/10/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
