Jason Fox

Icon

programming, products, and pontifications…

XML-RPC, SOAP and Polymorphism

According to the XML-RPC specification a XML_RPC request may only contain scalar <value>s or non-scalar <struct>s. The specification unfortunately does not provide any standard for encoding the type of data encoded in the <struct>s. This has the side effect of not being able to support polymorphism in service method parameters as it leaves the sever no choice but to rely on the method signature in the API declaration when trying to determine what to instantiate for a given <struct> in the XML-RPC request.

Let’s say you have the following declarations:

class SubscriptionsApi < ActionWebService::API::Base
  api_method(
    :create_subscription,
    :expects => [
      { :customer => Logical::Customer },
      { :payment_method => Logical::PaymentMethod }
    ]
  )
end
module Logical
  class PaymentMethod < ActionWebService::Struct
  end
  class CreditCard < PaymentMethod
    member :card_number, :string
    # ...
  end
  class PayPal < PaymentMethod
    member :login, :string
    # ...
  end
end

Now you want to make a call to the service method and pass either a CreditCard or a PayPal. XML-RPC will encode the request like so:

<methodCall>
  <methodName>create_subscription</methodName>
  <param>
    <struct>
      <member>
        <name>card_number</name>
        <value>4111-1111-1111-1111</value>
      </member>
    </struct>
  </param>
</methodCall>

This provides no type information to the server so the server will attempt to instantiate a Logical::PaymentMethod which will of course not have a card_number member as it’s specific to the CreditCard subclass. SOAP, on the other hand, does encode the parameter types allowing you to utilize this type of polymorphism in your service parameters. Here’s the same request encoded in SOAP.

<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
  <n1:CreateSubscription xmlns:n1="urn:ActionWebService" env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <payment_method xmlns:n2="http://www.ruby-lang.org/xmlns/ruby/type/custom" xsi:type="n2:Logical..CreditCard">
      <card_number xsi:type="xsd:string">1</card_number>
    </payment_method>
  </n1:CreateSubscription>
 </env:Body>
</env:Envelope>

The current implementation of ActionWebService resurrected by datanoise did not support this type of polymorphism in SOAP requests. However, I submitted a patch recently which provides for this functionality. Hopefully it’s accepted. :)