From XML-RPC to SOAP: A Migration Guide
December 18, 2002
What is XML-RPC?
As you might expect from the name, XML-RPC is a way of using XML to send classic Remote Procedure Calls (RPC) over the net. XML-RPC's use of XML is very simple. It doesn't use namespaces. It doesn't even use attributes.
Poking at the reasons behind technology standards can lead to interesting results. The really good ones, like the first ANSI C specification, include a detailed rationale for key decisions. Most internet standards don't spend the effort but prefer, instead, to allow an archived mailing list to act as a primary source. And, even then, it can be fun to play standards archaeologist.
The primary motivator behind XML-RPC is Dave Winer, owner of a small ISV company, and one of the first webloggers. Winer was part of the initial, self-selected group that created SOAP. According to Don Box's Brief History of SOAP, Winer grew impatient with corporate delays. In July, 1998, Winer posted the specification for XML-RPC. At that point, the Namespaces in XML group had published two drafts; final recommendation publication wouldn't happen until January, 1999. Given the state of flux, and all the churn caused by following XML Schema drafts which Don describes in his article, it isn't surprising that XML-RPC avoids namespaces altogether.
It is surprising, however, that XML-RPC doesn't use XML attributes. One might surmise that doing without attributes makes parsing much simpler. XML-RPC is well-suited to simple lexical handling: divide the input stream into "tags", which are simple words surrounded by angle brackets, and values. In hindsight, however, it makes for strange XML. For example, compare
<value> <i4>42</i4> </value>
For the first few years while SOAP cooked, XML-RPC met an important need. XML parsers -- particularly those in the common scripting languages -- were simple, and it was usually easy to create an XML-RPC client library (e.g., the Python implementation is less than 900 lines) and to get remote access to services over the Web.
But let's move forward a few years. Now SOAP 1.1 is available for just about any platform you could care about. SOAP 1.2 is almost done -- if they ever get it out of last call -- and we've already passed over the hype curve for web services. The time has come for the XML-RPC community to move forward; the time has come to migrate to SOAP, achieving interoperability across both systems. Let's see how easy it can be.
Wrapping XML-RPC in SOAP
The first step is to wrap XML-RPC messages in a SOAP envelope, which is trivial. We'll treat the wrapped message as a pure XML document and put it in a SOAP body:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/envelope/"> <S:Body xmlns=""> [XML-RPC content here] </S:Body> </S:Envelope>
The XML-RPC implementation are drop-dead simple.
- Ignore the first two open tags.
- Ignore the last two close tags.
- If you don't handle namespaces, assume the client got it right.
This doesn't achieve full SOAP interoperability, but that's not our goal. Our goal
SOAP clients to be able to send messages to "XML-RPC servers" and for those messages
understood. Wrapping XML-RPC in a SOAP
Body achieves the goal. (Making sure
that the SOAP clients don't send anything surprising will be explained below.)
The next step is to consider the way SOAP and XML-RPC both use -- or, perhaps, abuse -- HTTP. According to the XML-RPC specification,
- the URL is a routing hint;
- calls must have a
- calls must have a
Once again, we're in luck. SOAP can use
POST, and it does not specify a
particular value for the URL. For implementations that don't allow the application
User-Agent header, sending the message through a
nearby HTTP proxy should suffice. To be truly liberal in what we accept, however,
add the following to our list of XML-RPC implementation changes:
- Don't reject messages that are missing
XML-RPC servers sending responses or XML-RPC clients talking to SOAP servers need only wrap their messages in the SOAP elements as shown above.
So far all the work to achieve interoperability has put the effort -- however minor -- on the XML-RPC community. It's only fair to ask what the SOAP community has to do. It turns out that most of the work will be in defining constraints so that the SOAP libraries generate messages that their new brethren can understand.
As a practical exercise in achieving interoperability, we'll use WSDL (and, therefore, XML Schema) to specify the XML-RPC encapsulation.
We start with a WSDL file. It begins with the typical large block of namespace declarations (though it's only about half of what DevStudio.NET seems to use). Since all XML-RPC messages have the same "shape", it's all one set of message and port types for WSDL. We'll get into schema details in a moment.
<definitions name="http://www.xml.com/endpoints/xmlrpcwrap/" targetNamespace="http://www.xml.com/endpoints/xmlrpcwrap/" xmlns:tns="http://www.xml.com/endpoints/xmlrpcwrap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns="http://schemas.xmlsoap.org/wsdl/"> <types> <xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.xml.com/endpoints/xmlrpcwrap/"> ... </xsd:schema> </types> <message name="XMLRPCRequest"> <part name="body" type="tns:Request"/> </message> <message name="XMLRPCResponse"> <part name="body" type="tns:Response"/> </message> <portType name="XMLRPCPort"> <operation name="XMLRPCExchange"> <input message="tns:XMLRPCRequest"/> <output message="tns:XMLRPCResponse"/> </operation> </portType>
As I've discussed before, WSDL
is a frustrating mix of magic redundant verbosity and often needless abstraction.
need to write a WSDL
binding element to specify how to instantiate the abstract
message exchanges. It's fairly simple: SOAP over HTTP as uninterpreted XML documents.
WSDL jargon, that's "doc/literal" -- an XML-document transported literally, without
<binding name="XMLRPCBinding" type="tns:XMLRPCPort"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="XMLRPC"> <soap:operation soapAction="RSalz rocks"/> <input> <soap:body use="literal" namespace="http://www.xml.com/endpoints/xmlrpcwrap/"/> </input> <output> <soap:body use="literal" namespace="http://www.xml.com/endpoints/xmlrpcwrap/"/> </output> </operation> </binding>
service part is where it might get interesting. An actual service element
is fairly simple.
<service name="XMLRPC"> <port name="XMLRPC" binding="tns:XMLRPCBinding"> <soap:address location="http://www.example.com/xmlrpc"/> </port> </service>
At issue here is the fact that different XML-RPC servers may require particular URLs for their operation dispatch. While the example above shows a generic interface, some require the "method name" to be the last component of the URL.
In this case the most reasonable thing to do is split the WSDL into separate pieces
XMLRPCTypes.xsd, XMLRPCabstract.wsdl (the
binding elements) -- that can be imported into an application-specific file
port elements with the appropriate URLs.
For those who really enjoy holiday fables, you can figure out how to put such information into UDDI.
XML Schema for XML-RPC
Let's now flesh out the XML Schema that defines XML-RPC messages.
An XML-RPC request is a complex element named
methodCall, which has a name
identifying the method and a
params element with a list of input
<xsd:complexType name="methodCallType"> <xsd:sequence> <xsd:element name="methodName" type="xsd:string"/> <xsd:element name="params" minOccurs="0"> <xsd:element name="param" type="tns:paramType" minOccurs="0" maxOccurs="unbounded"/> </xsd:element> </xsd:sequence> </xsd:complexType> <element name="methodCall" type="tns:methodCallType"/>
According to the specification, the methodName is limited to the characters
"[A-Za-z0-9_.:/]". Rewriting the
complexType to include that facet restriction
is left as an exercise for the reader.
The primitive XML-RPC parameters are pretty much a subset of the XML Schema datatypes:
<xsd:complexType name="typedscalarType"> <xsd:choice> <xsd:element name="i4" type="xsd:int"/> <xsd:element name="base64" type="xsd:base64Binary"/> <xsd:element name="double" type="xsd:float"/> ... </xsd:choice> </xsd:complexType>
The others --
-- should map pretty easily into XML Schema types, perhaps with additional facet
XML-RPC also provides
array to build up more complex
datatypes. A structure is an array of members, each of which has a name and typed
<xsd:complexType name="structType"> <xsd:sequence> <element name="member" type="structmemberType" minOccurs="0" maxOccurs="unbounded"/> </xsd:sequence </xsd:complexType> <xsd:complexType name="structmemberType"> <xsd:sequence> <xsd:element name="name" type="xsd:string"/> <xsd:element name="value" type="typedscalarType"/> </xsd:sequence> </xsd:complexType>
Arrays are like structs, except there is a single
data element that has a list
value items. Playing standards archaeologist again, it would be interesting
to know what problem the extra level of indirection was meant to solve.
XML-RPC experts will realize that I've left out a couple of twists that make things
little more complicated. First, any value that doesn't have a type is a string. Second,
array are mutually recursive;
like any reasonable programming language, it's possible to have an array of structures
simple values and nested arrays. Solving this requires delving into some of the more
areas of XML Schema, which is also left as an exercise for the reader.
Ho ho ho
So where are we? With a few simple changes, XML-RPC applications should be able to send and receive SOAP messages to and from well-behaved SOAP applications. Perhaps surprisingly, most of the work is in constraining the SOAP applications to be well-behaved. In the standards world, when a specification has many options and possibilities, and you define yourself to a conformant subset, that's called a profile. So what we've started work on is a SOAP profile, defined in WSDL, that should make it easy for "legacy" XML-RPC applications to interoperate.
I've only sketched out the framework; the devil inhabits the details. But it certainly seems possible to achieve what John Lennon sung about:
I hope some day you'll join us
and the world will be as one.