WebReference.com - Chapter 30 of Curl Programming Bible, from John Wiley & Sons (7/8)
[previous] [next] |
Curl Programming Bible, chapter 30
Creating a custom mapping for a struct
The SOAP specification defines a struct as a compound value in which the accessor name is the only distinction among member values, and no accessor has the same name as any other. More simply, a struct is an aggregate of values of arbitrary types, each value identified by a unique name.
Consider a struct that contains two values, both of type string
. One value of the struct is identified by IP
and the other by Hostname
. The SOAP encoding rules represent a struct as a sequence of elements. The names of the elements are names associated with the values. So, the SOAP encoding for struct would look like this:
<IP>167.216.248.234</IP>
<Hostname>www.curl.com</Hostname>
NOTE: A struct may have an arbitrary number of fields and each field may have an arbitrary type. While our example has fields of the same type, this is not the general case for
struct
s.
Suppose you want to call a Web service that performs a DNS lookup. The SOAP operation takes a single input argument and single output argument. Its input argument is a string containing either an IP address or hostname and its output argument is a struct containing both an IP address and a hostname.
In order to call this DNS lookup Web service, write a general-purpose class named Soap-1-1-StructureDescriptor
to handle the mapping of struct
s. The class handles the marshaling and unmarshaling between the struct and the corresponding Curl type. The constructor for the class takes the XML name for the struct, the corresponding Curl type, and a Soap-1-1-StandardArgumentDescriptor
for each of the fields of the struct. For the struct just shown, the call to the constructor is
{let structure-descriptor:Soap-1-1-StructureDescriptor =
{new Soap-1-1-StructureDescriptor,
{new XMLName, "", "InternetAddress"},
InternetAddress,
{new Soap-1-1-StandardArgumentDescriptor,
{new XMLName, "", "IP"},
{new XMLName, xsd, "string"},
String
},
{new Soap-1-1-StandardArgumentDescriptor,
{new XMLName, "", "Hostname"},
{new XMLName, xsd, "string"},
String
}
}
}
NOTE: We are using the accessor names of the struct fields in the
Soap-1-1-StandardArgumentDescriptor
passed to theSoap-1-1-StructureDescriptor
constructor. This works because the encoding rules for arguments are similar to the encoding rules forstruct
s. In either case, the accessor or argument name is used as the tag name of the element that encloses the field or argument value.
Below are the field declarations and the constructor for the Soap-1-1-StructureDescriptor
class. The constructor is called with the name for the XML type of the struct, the corresponding Curl type for the struct, and a sequence of Soap-1-1-StandardArgumentDescriptor
s, one for each field in the struct.
{define-class public Soap-1-1-StructureDescriptor
|| mapping between curl-type and xml-type
field public-get private-set curl-type:Type
field public-get private-set xml-type:XMLName
|| element descriptors
field public-get private-set element-descriptors: {Array-of
Soap-1-1-StandardArgumentDescriptor}
field public-get private-set marshal:XMLMarshaler
field public-get private-set unmarshal:XMLUnmarshaler
{constructor public {default
xml-type:XMLName,
curl-type:Type,
...:Soap-1-1-StandardArgumentDescriptor
}
set self.curl-type=curl-type
set self.xml-type=xml-type
set self.element-descriptors={new {Array-of Soap-1-1-StandardArgumentDescriptor}}
{for element-descriptor:Soap-1-1-StandardArgumentDescriptor in ... do
{self.element-descriptors.append element-descriptor}
}
Soap-1-1-StructureDescriptor
uses the Curl supplied class named Arguments
. Arguments
is the class used to represent an argument list in Curl. Curl allows both positional and keyword arguments so the Arguments
class can represent both kinds of arguments. Soap-1-1-StructureDescriptor
only uses keyword arguments in Arguments
values.
The Soap-1-1-StrucutreDescriptor
class makes some assumptions about the Curl type that maps to the struct. It assumes it has a method that converts the value of the Curl type to the Arguments
type. The value of the Arguments type contains keyword arguments of the form name=value
, where name corresponds to an accessor name and value is its corresponding value. The Soap-1-1-StructureDescriptor
class also assumes the Curl type has a constructor that accepts keyword arguments of the form name=value
, where name is an accessor name and value is the corresponding value. Here's the Curl class InternetAddress
that corresponds to the struct shown above and adheres to the assumptions of the Soap-1-1-StructureDescriptor
class:
{define-class public InternetAddress {inherits SoapStruct}
field public ip:String
field public hostname:String
{constructor public {default IP:String="", Hostname:String=""}
set self.ip=IP
set self.hostname=Hostname
}
{method public {to-Arguments}:Arguments
{return
{Arguments
IP=self.ip,
Hostname=self.hostname
}
}
}
}
The SoapStruct
class is an abstract class containing the to-Arguments
method. The Soap-1-1-StructureDescriptor
class assumes that the Curl type that maps the struct inherits the SoapStruct
class. Here is the SoapStruct
class:
{define-class public abstract SoapStruct
{method public abstract {to-Arguments}:Arguments}
}
The interesting part of Soap-1-1-StructureDescriptor
is the marshaling and unmarshaling procedure it provides to convert between the struct and the corresponding Curl type. Consider the unmarshaling procedure:
set self.unmarshal =
|| all unmarshler must be of this proc-type
{proc {map:XMLCurlMappingRegistry,
encoding-style:String,
in:XMLInputStream
}:any
|| we'll collect the XML struct into Arguments
|| in the form of <field-name>=<value>
let struct:Arguments = {Arguments}
let done?:bool = false
|| read the fields of the XML struct
|| what we're expecting is a series a values
|| embedded in element where the tag name of
|| the element is the field name in the struct
{until tag=read-struct-element, done? do
let xml-token:XMLToken = {in.read-one}
{type-switch xml-token
case end:XMLEndElement do
|| if it's the end of an element, we're done
{in.unread-one end}
set done? = true
case start:XMLStartElement do
|| it's the start of an element -- another field of the struct
{for element-descriptor:Soap-1-1-StandardArgumentDescriptor in self.element-descriptors do
|| match the tag name of the element with a name in the descriptor
{if start.element == element-descriptor.name then
|| skip over attributes
{while true do
let xml-token:XMLToken={in.read-one}
{type-switch xml-token
case attribute:XMLAttribute do
{continue}
else
{in.unread-one xml-token}
{break}
}
}
|| unmarshal the value using the information in descriptor
let value:any =
{map.unmarshal
encoding-style,
element-descriptor.curl-type,
element-descriptor.xml-type,
in
}
|| matching end element
let end:XMLEndElement = {in.read-one} asa XMLEndElement
|| append <field-name>=<value>
{struct.append keyword = element-descriptor.name.local-name, value}
{continue tag=read-struct-element}
}
}
|| element name doesn't match name in descriptor
{error "unrecognized element name:" & start.element}
else
{error "unexpected XMLToken: " & xml-token}
}
}
let result:any =
|| Curl type is expected to be substype of SoapStruct
{if {self.curl-type.subtype-of? SoapStruct} then
let t:ClassType = self.curl-type asa ClassType
|| call class constructor with <field-name>=<value> list
|| compiled from XML str
{t {splice struct}}
else
{error "expect subclass of SoapStruct"}
}
{return result}
t}
}
The Soap-1-1-StructureDescriptor
provides a method to register the marshaling and unmarshaling procedures that map between the struct and the corresponding Curl type. The method takes an XMLCurlMappingRegistry
as an argument. Here is the call to the DNS lookup Web service:
|| create a registry
{let my-registry:XMLCurlMappingRegistry = {new XMLCurlMappingRegistry}}
|| register the struct mapping
{structure-descriptor.register my-registry}
|| make the SOAP call
{let anys:{Array-of any}={DNS-lookup-operation.call
"www.curl.com",
registry=my-registry
}
}
|| display the results
Hostname: {value (anys[0] asa InternetAddress).hostname}
{br}
IP address: {value (anys[0] asa InternetAddress).ip}
The full applet (structure-descriptor.curl
) is on the accompanying Web site.
[previous] [next] |
Created: August 14, 2002
Revised: August 14, 2002
URL: https://webreference.com/programming/curlbible/chap30/7.html