Thanks!
proc EnvelopeWrapper {procVarName args} {
upvar $procVarName procvar
set procName [lindex [split $procVarName {_}] end]
set params $procvar(params)
set name $procvar(name)
set uri $procvar(uri)
set soapenv $procvar(version)
set soapenc $procvar(encoding)
# Check for options (ie: -header) give up on the fist non-matching
arg.
array set opts {-headers {} -attributes {}}
while {[string match -* [lindex $args 0]]} {
switch -glob -- [lindex $args 0] {
-header* {
set opts(-headers) [concat $opts(-headers) [lindex
$args 1]]
set args [lreplace $args 0 0]
}
-attr* {
set opts(-attributes) [concat $opts(-attributes)
[lindex $args 1]]
set args [lreplace $args 0 0]
}
-- {
set args [lreplace $args 0 0]
break
}
default {
# stop option processing at the first invalid option.
break
}
}
set args [lreplace $args 0 0]
}
# check for variable number of params and set the num required.
if {[lindex $params end] == "args"} {
set n_params [expr {( [llength $params] - 1 ) / 2}]
} else {
set n_params [expr {[llength $params] / 2}]
}
# check we have the correct number of parameters supplied.
if {[llength $args] < $n_params} {
set msg "wrong # args: should be \"$procName"
foreach { id type } $params {
append msg " " $id
}
append msg "\""
return -code error $msg
}
set doc [dom::DOMImplementation create]
set envx [dom::document createElement $doc "SOAP-ENV:Envelope"]
dom::element setAttribute $envx "xmlns:SOAP-ENV" $soapenv
dom::element setAttribute $envx "xmlns:SOAP-ENC" $soapenc
dom::element setAttribute $envx "SOAP-ENV:encodingStyle" $soapenc
# The set of namespaces depends upon the SOAP encoding as specified
by
# the encoding option and the user specified set of relevant
schemas.
foreach {nsname url} [concat \
[::rpcvar::default_schemas $soapenc] \
$procvar(schemas)] {
if {! [string match "xmlns:*" $nsname]} {
set nsname "xmlns:$nsname"
}
dom::element setAttribute $envx $nsname $url
}
# Insert the Header elements (if any)
if {$opts(-headers) != {}} {
set headelt [dom::document createElement $envx
"SOAP-ENV:Header"]
foreach {hname hvalue} $opts(-headers) {
set hnode [dom::document createElement $headelt $hname]
::SOAP::insert_value $hnode $hvalue
}
}
# Insert the body element and atributes.
# ########################################################
# Change the else condition below
# #######################################################
set bod [dom::document createElement $envx "SOAP-ENV:Body"]
if {$uri == ""} {
# don't use a namespace prefix if we don't have a namespace.
set cmd [dom::document createElement $bod "$name" ]
} else {
set cmd [dom::document createElement $bod "ns:$name" ]
dom::element setAttribute $cmd "xmlns:ns" $uri
}
# Insert any method attributes
if {$opts(-attributes) != {}} {
foreach {atname atvalue} $opts(-attributes) {
dom::element setAttribute $cmd $atname $atvalue
}
}
# insert the parameters.
set param_no 0
foreach {key type} $params {
set val [lindex $args $param_no]
set d_param [dom::document createElement $cmd $key]
::SOAP::insert_value $d_param [::rpcvar::rpcvar $type $val]
incr param_no
}
# We have to strip out the DOCTYPE element though. It would be
better to
# remove the DOM node for this, but that didn't work.
set prereq [dom::DOMImplementation serialize $doc]
set req {}
dom::DOMImplementation destroy $doc ;# clean up
regsub "<!DOCTYPE\[^>\]*>\r?\n?" $prereq {} req ;# hack
set req [encoding convertto utf-8 $req] ;# make it UTF-8
return $req ;# return the XML
data
}
SOAP::create SomeMethod -proxy
"http://localhost/superSOAP/webcontrol.asmx" \
-action
"http://www.atune.biz/web/services/WC/2006-1/isWebUserAuthenticated" \
-uri "http://www.atune.biz/web/services/WC/2006-01" \
-params {WebUser webUser}
SOAP::configure SomeMethod -wrapProc EnvelopWrapper
instead of:
<SOAP-ENV:Body>
<ns:MSLR
xmlns:ns="urn:ms">
<username
xsi:type="xsd:string">
String
</username>
<password
xsi:type="xsd:string">
String
</password>
</ns:MSLR>
</SOAP-ENV:Body>
Notice how everything is preceded with "ns". Is there an easier way to
do what I want? Thanks again!
# ########################################################
# Change the else condition below Notice I commented out the ns
namespace.
# #######################################################
set bod [dom::document createElement $envx "SOAP-ENV:Body"]
if {$uri == ""} {
# don't use a namespace prefix if we don't have a namespace.
set cmd [dom::document createElement $bod "$name" ]
} else {
# set cmd [dom::document createElement $bod "ns:$name" ]
# dom::element setAttribute $cmd "xmlns:ns" $uri
}
An alternative that I like to use when I have actual XML samples
of the SOAP requests is to have the -wrapProc simply substitute the
appropriate values into an XML template. See my comment (as "lexfiend")
in <http://wiki.tcl.tk/1761> for details.
- Adrian
Scott Nichols correctly pointed out that my code fragment didn't do
XML character-escaping (and perhaps other stuff as well). If anyone's
actually using that code, I've updated it accordingly in the Wiki page.
Do try it and point out anything else I may have missed.
- Adrian
> Hi,
> Thanks for the response. It seems like that's a lot to do for the end
> result I want. Here's the relevant part of the SOAP envelope that I'm
> trying to produce:
> <SOAP-ENV:Body>
> <m:MSLR
> xmlns:m="urn:ms">
> <m:username>
> String
> </m:username>
> <m:password>
> String
> </m:password>
> </m:MSLR>
> </SOAP-ENV:Body>
>
> instead of:
>
> <SOAP-ENV:Body>
> <ns:MSLR
> xmlns:ns="urn:ms">
> <username
This should probably be <ns:username
> xsi:type="xsd:string">
> String
> </username>
> <password
This should probably be <ns:password
> xsi:type="xsd:string">
> String
> </password>
> </ns:MSLR>
> </SOAP-ENV:Body>
> Notice how everything is preceded with "ns". Is there an easier way to
> do what I want? Thanks again!
It seems to me that anything that cares that the namespace prefix is
'ns' rather than 'm' is broken, namespace prefixes are not the
namespace identifier, the given urn is the namespace identifier. The
W3C seem to agree with me as well.
I don't think that your problem is really 'ns' rather than 'm', I think
your problem is that in the first instance 'username' and 'password'
are in the specified namespace, whereas in the second they are not.
This is probably what you need to fix.
Dan
Scott