In the process of binding a fancy SOAP stack to JBoss-Rails, I of course became frustrated working with DOM trees or StAX iterators. If you're using 100% Pure Java(tm), you'd use JAXB, and compile up a nice tree matching the object-model described by the XSD in the WSDL.
I, on the other hand, am not using 100% Pure Java(tm) syntax and processes. I'm dynamically deploying WSDLs handled by dynamically reloadable Ruby classes.
So I need a dynamic data-binding to wire up to the Apache-CXF chain. A CXF DataBinding is responsible for handling readers and writers capable of converting an XML stream into some other object. And back again. The data-binding is informed by the XML Schema used to define the web-service.
Now, some examples using the Amazon EC2 WSDL.
The describe-instances operation is used to list information about running server instances on EC2. The request message is defined by this XSD fragment:
<xs:element name="DescribeInstances"
type="tns:DescribeInstancesType"/>
<xs:complexType name="DescribeInstancesType">
<xs:sequence>
<xs:element name="instancesSet"
type="tns:DescribeInstancesInfoType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DescribeInstancesInfoType">
<xs:sequence>
<xs:element name="item"
type="tns:DescribeInstancesItemType"
minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DescribeInstancesItemType">
<xs:sequence>
<xs:element name="instanceId"
type="xs:string" />
</xs:sequence>
</xs:complexType>
When deployed (at runtime) the Ruby data-binding swizzles that into some relatively simple Ruby classes:
class DescribeInstancesType
attr_accessor :instancesSet
def initialize()
@instancesSet = DescribeInstancesInfoType.new()
end
end
class DescribeInstancesInfoType < ::Array
end
class DescribeInstancesItemType
attr_accessor :instanceId
def initialize()
@instanceId = ''
end
end
Through some magic involving subclassing ::Array, when a portion of the tree is array-like, we end up with a very intuitive-to-use model. We've borrowed this strategy from xsd2ruby by the soap4r guys.
Throwing some XML at it:
<ns1:DescribeInstances>
<ns1:instancesSet>
<ns1:item>
<ns1:instanceId>foo</ns1:instanceId>
</ns1:item>
<ns1:item>
<ns1:instanceId>bar</ns1:instanceId>
</ns1:item>
<ns1:item>
<ns1:instanceId>baz</ns1:instanceId>
</ns1:item>
</ns1:instancesSet>
</ns1:DescribeInstances>
Results in the Ruby object:
#<DescribeInstancesType:0x57581ef
@instancesSet=[#<DescribeInstancesItemType:0x23356034
@instanceId="foo">,
#<DescribeInstancesItemType:0x242e34f2
@instanceId="bar">,
#<DescribeInstancesItemType:0x6dc22478
@instanceId="baz">]>
And using it is quite simple:
root.instancesSet.each{|e| puts e.instanceId }
Likewise, in a day or two, we'll have the reverse, capable of marshalling a Ruby object right back to some XSD-defined XML.
And all of this will be used in the JBoss-Rails web-services bits. It's all driven by the Java StAX parsers, so no need to argue about "REXML is slow!" or whatnot. All XML-handling occurs from Java, against fast Java XML parsers.