环境变量定义好后就可以重复使用而不必再次初始化了。只有当线程独占访问时,我们才需要一个新的环境变量。例如,下面的代码分配了一个用于多个远程方法的环境变量:
int main()
{
struct soap soap;
soap_init(&soap); // 初始化环境变量
...
soap_call_ns__method1(&soap, ...); // 调用一个远程方法
soap_call_ns__method2(&soap, ...); // 调用另一个远程方法
soap_end(&soap); // 清除环境变量
}
我们也可以像下面这样定义环境变量:
int main()
{
struct soap *soap;
soap = soap_new(); // 定义并初始化环境变量
if (!soap) // 如果不能定义,退出
soap_call_ns__method1(soap, ...); // 调用远程函数
soap_call_ns__method2(soap, ...); // 调用另一个远程函数
soap_end(soap); // 清除环境变量
free(soap); // 释放环境变量空间
}
服务端代码在调用soap_serve函数前,需要定义相关环境变量:
int main()
{
struct soap soap;
soap_init(&soap);
soap_serve(&soap);
}
或者像下面这样:
int main()
{
soap_serve(soap_new());
}
soap_serve函数用来处理一个或多个(当允许HTTP keep-alive时,参见18.11节中的SOAP_IO_KEEPALIVE标志)请求。
一个web服务可以用多线程技术来处理请求:
int main()
{
struct soap soap1, soap2;
pthread_t tid;
soap_init(&soap1);
if (soap_bind(&soap1, host, port, backlog) < 0) exit(1);
if (soap_accept(&soap1) < 0) exit(1);
pthread_create(&tid,NULL, (void*(*)(void*))soap_serve, (void*)&soap1);
soap_init(&soap2);
soap_call_ns__method(&soap2, ...); // 调用远程方法
soap_end(&soap2);
pthread_join(tid, NULL); // 等待线程结束
soap_end(&soap1); // 释放环境变量
}
在上面的例子中,需要两个环境变量信息。而在1.x版本中,由于静态分配环境变量,多线程技术是不被允许的(只有一个线程可以用这个环境变量调用远程方法或处理请求信息)。
8.2.4节将给出一个具体的多线程服务实例,它为每个SOAP请求分配一个独立线程进行处理。
2.7 gSOAP文档翻译计划(6)
gSOAP 使用下面的软件包验证了其可用性: Apache 2.2 Apache Axis ASP.NET Cape Connect Delphi easySOAP++ eSOAP Frontier GLUE IonaXMLBus kSOAP MS SOAP Phalanx SIM SOAP::Lite SOAP4R Spray SQLData Wasp Adv. Wasp C++ White Mesa xSOAP ZSI 4S4C
2.8 gSOAP文档翻译计划(7)
准备工作:
要开始用gSOAP创建一个web服务应用, 你需要:
l 一个C/C++编译器.
l 拥有根据操作系统平台创建的可执行的gSOAP的stdsoap2(windows下为stdsoap2.exe)编译器。
l 拥有根据操作系统平台创建的可执行的gSOAP的wsdl2h(windows下为wsdl2h.exe)WSDL解析器。
l 需要 ' stdsoap2.c ' 或 ' stdsoap2.cpp ' 及 ' stdsoap2.h ' 文件来实现你的SOAP功能。你可以创建一个dll或动态库以便简化连接。
l 如果你要支持SSL(HTTPS)及压缩的话,可以安装OpenSSL及Zlib库。
l gSOAP是独立开发包,不需要任何第三方的软件支持(除非你要用到OpenSSL及Zlib)。
l 与平台无关的gSOAP版本需要你下面的工具编译 ' soapcpp2 ' 及 ' wsdl2h ' 文件:
l 一个C++编译器(用来编译 ' wsdl2h ' WSDL解析器)。
l Bison 或 Yacc;Flex 或 Lex推荐使用Bison及Flex。
l 在软件包samples目录下有大量的开发实例。可以用 ' make ' 来编译这些例子。这些例子包含了gSOAP中的各个方面。其中,最简单的例子是one-liners(samples/oneliners)。
2.9 gSOAP文档翻译计划(8.1.1)快速指南
本指南旨在让你快速开始你的gSOAP开发之旅。阅读本节的内容,需要你对SOAP 1.1协议及C/C++语法有大体的了解。虽然使用gSOAP编译器可以直接用C/C++开始编写web服务及客户端程序而不需要了解SOAP协议的细节,但是由于我们在本节中使用了大量的实例来说明gSOAP与其他SOAP实现的连接及通讯,所以了解一些SOAP及WSDL协议也是必需的。
8.1 如何使用gSOAP编译环境来编译SOAP客户端程序
通常,一个SOAP客户端应用的实现需要为每个客户端需要调用的远程方法提供一个存根例程(stub routine)。存根例程主要负责编码参数信息;将包含参数信息的调用请求发送给制定的SOAP服务;等待返回结果;将结果中的参数信息编码。客户端程序调用访问远程方法的存根例程就像调用本地方法一样。用C/C++手工别写一个存根例程是个十分痛苦的差使,尤其当远程方法的参数中包含特定的数据结构(如:记录、数组、图等)时。幸运的是,gSOAP包中的 ' wsdl2h ' WSDL解析器和 ' soapcpp2’存根及架构编译器能够将web服务客户端及服务端的开发工作自动化。
' soapcpp2’存根及架构编译器是可以生成构建C++ SOAP客户端所需的C++源码的预编译器。该预编译器的输入参数是一个标准的C/C++头文件。这个头文件可以由WSDL解析器根据相关的WSDL文档自动生成。
参见下面的命令:
$ wsdl2h -o quote.h http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl
上面的命令根据制定URL提供的WSDL文档生成一个C++语法结构的头文件。
如果需要生成一个纯C的头文件,需要一下命令:
$ wsdl2h -c -o quote.h http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl
更多关于WSDL解析器及其选项的细节信息,请参见8.2.10节。执行上述命令后,quote.h文件就生成了。其中包含开发客户端或服务端程序的存根例程定义。SOAP服务远程方法以函数声明的方式在这个头文件中被定义。C/C++源代码的存根例程将通过预编译器自动实现。同时,每个远程方法的程序框架也被自动生成了,它可以用来建立SOAP服务端程序应用。SOAP服务的输入输出参数可以是简单的数据类型或复杂的数据结构,可以由WSDL解析器自动生成或手工定义。预编译器将自动生成序列化/反序列化这些数据的代码,以便存根例程可以将这些数据以XML的方式编码或解码。
8.1.1 例子
XMethods Delayed Stock Quote 服务提供一个getQuote方法(由 ' wsdl2h ' WSDL解析器生成的quote.h定义)。这个方法根据提供的股票名称返回相应的股票价格。下面是这个方法的WSDL文档信息:
Endpoint URL: http://services.xmethods.net:80/soap
SOAP action: "" (2 quotes)
Remote method namespace: urn:xmethods-delayed-quotes
Remote method name: getQuote
Input parameter: symbol of type xsd:string
Output parameter: Result of type xsd:float
下面是由WSDL解析器生成的getQuote.h头文件(实际的文件内容与 ' wsdl2h ' 版本及生成选项有关):
//gSOAP ns1 service name: net_DOTxmethods_DOTservices_DOTstockquote_DOTStockQuoteBinding
//gSOAP ns1 service type: net_DOTxmethods_DOTservices_DOTstockquote_DOTStockQuotePortType
//gSOAP ns1 service port: http://66.28.98.121:9090/soap
//gSOAP ns1 service namespace: urn:xmethods-delayed-quotes
//gSOAP ns1 service documentation: Definitions generated by the gSOAP WSDL parser 1.0
// Service net.xmethods.services.stockquote.StockQuoteService : net.xmethods.services.stockquote.StockQuote web service
//gSOAP ns1 service method-style: getQuote rpc
//gSOAP ns1 service method-encoding: getQuote http://schemas.xmlsoap.org/soap/encoding/
//gSOAP ns1 service method-action: getQuote urn:xmethods-delayed-quotes#getQuote
int ns1__getQuote(char *symbol, float &Result);
这个头文件用C/C++代码为gSOAP预编译器指定了web服务的细节。远程方法被定义为函数ns1__getQuote,同时指定了客户端调用web服务所需
的所有细节信息。
getQuote远程方法需要一个名为symbol的字符串作为输入参数,同时需要一个名为Result的浮点数作为输出参数。预编译器生成的远程方法调用函数中,最后一个参数必须是输出参数,该参数以引用方式传递或定义为指针类型。除此之外的所有参数都是输入参数,这些参数必须以传值方式传递。函数返回一个整型值,其值说明web服务调用成功或出现的错误。具体的错误代码信息参见10.2节。
函数名中命名空间前缀ns1__的细节信息将在8.1.2节中讨论。简单的说,命名空间前缀与函数名之间用两个下划线分割。比如,ns1__getQuote中,ns1为命名空间前缀,getQuote是函数名称。(函数名中单个下划线将在XML中解释为破折号-,因为在XML中破折号比下划线更常用,细节请参见10.3节)用下面命令执行预编译器:
soapcpp2 getQuote.h
预编译器根据getQuote.h中定义的信息来生成存根例程的代码框架。这个存根例程可以在客户端程序中随处调用。存根例程被声明为下面的样子:
int soap_call_ns1__getQuote(struct soap *soap, char *URL, char *action, char *symbol, float &Result);
存根例程保存在soapClient.cpp文件中;soapC.cpp文件包含了序列化、反序列化数据的函数。你可以用 -c编译选项来生成纯C的代码。注意,soap_call_ns1__getQuote在ns1__getQuote的参数基础上又增加了三个参数:soap必须是指向一个gSOAP运行环境的合法指针;URL是web服务的URL;action指明了web服务需要的SOAP action。XMethods Delayed Stock Quote服务的URL是http://66.28.98.121:9090/soap,action是"" (2 quotes)。你可以动态的改变URL及action。如果上述两个变量定义为NULL,则会使用头文件中定义的信息。下面的例子调用远程方法获取IBM的股票信息:
#include "soapH.h" // 包含生成的存根例程定义
#include "net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding.nsmap" // 包含命名空间表
int main()
{
struct soap soap; // gSOAP运行环境
float quote;
soap_init(&soap); // 初始化运行环境(只执行一次)
if (soap_call_ns1__getQuote(&soap, NULL, NULL, "IBM", "e) == SOAP_OK)
std::cout << "Current IBM Stock Quote = " << quote << std::endl;
else // an error occurred
soap_print_fault(&soap, stderr); // 在stderr中显示错误信息
soap_destroy(&soap); // 删除类实例(仅用于C++中)
soap_end(&soap); // 清除运行环境变量
soap_done(&soap); // 卸载运行环境变量
return 0;
}
调用成功后,存根例程返回SOAP_OK同时quote变量保存着股票信息;如果调用失败则产生一个错误,同时通过soap_print_fault函数显示错误信息。gSOAP编译器同时为C++客户端程序生成了一个代理类。
#include "soapnet_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBindingProxy.h" // 获得代理
#include "net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding.nsmap" // 包含命名空间表
int main()
{
net q; // "net" 是这个服务代理类的短名称
float r;
if (q.ns1__getQuote("IBM", r) == SOAP_OK)
std::cout << r << std::endl;
else
soap_print_fault(q.soap, stderr);
return 0;
}
代理类的构造函数定义并初始化了一个gSOAP环境变量实例。所有的HTTP及SOAP/XML处理被隐藏在后台处理。你可以改变WSDL解析器生成的头文件中web服务的名称。web服务的名字是由WSDL内容中萃取的,并不总是短名称格式的。你可以随意更改这个条目
//gSOAP ns1 service name: net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding来使用一个更合适的名字。这个名字将决定代理类文件及XML命名空间表文件的名字。
下面的函数可以用来建立一个gSOAP运行环境(struct soap):
soap_init(struct soap *soap) 初始化运行环境变量(只需要执行一次)
soap_init1(struct soap *soap, soap_mode iomode) 初始化运行环境变量同时设置in/out模式
soap_init2(struct soap *soap, soap_mode imode, soap_mode omode) 初始化运行环境变量同时分别设置in/out模式
struct soap *soap_new() 定义、初始化运行环境并返回一个执行运行环境的指针
struct soap *soap_new1(soap_mode iomode) 定义、初始化运行环境并返回一个执行运行环境的指针并设置in/out模式
struct soap *soap_new2(soap_mode imode, soap_mode omode) 定义、初始化运行环境并返回一个执行运行环境的指针并分别设置in/out模
式
struct soap *soap_copy(struct soap *soap) 定义一个新的环境变量并将现有环境信息赋值给新的变量
soap_done(struct soap *soap) 重置、关闭连接,清除环境变量
环境变量可以在程序中任意次数的使用。每个新的线程就需要一个新的环境变量实例。当例子中的客户端程序执行时,SOAP请求通过soap_call_ns1__getQuote函数来调用,它生成下面的SOAP RPC请求信息:
POST /soap HTTP/1.1
Host: services.xmethods.net
Content-Type: text/xml
Content-Length: 529
SOAPAction: ""
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns1="urn:xmethods-delayed-quotes"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getQuote>
<symbol>IBM</symbol>
</ns1:getQuote>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XMethods Delayed Stock Quote 服务返回如下的信息:
HTTP/1.1 200 OK
Date: Sat, 25 Aug 2001 19:28:59 GMT
Content-Type: text/xml
Server: Electric/1.0
Connection: Keep-Alive
Content-Length: 491
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/?
<soap:Body>
<n:getQuoteResponse xmlns:n="urn:xmethods-delayed-quotes?
<Result xsi:type="xsd:float?41.81</Result>
</n:getQuoteResponse>
</soap:Body>
</soap:Envelope>
服务返回的信息通过存根例程来解析,并保存在soap_call_ns1__getQuote函数的quote参数中。客户端程序可以在任意时间多次调用远程方法。请看下面的例子:
...
struct soap soap;
float quotes[3]; char *myportfolio[] = {"IBM", "MSDN"};
soap_init(&soap); // need to initialize only once
for (int i = 0; i < 3; i++)
if (soap_call_ns1__getQuote(&soap, "http://services.xmethods.net:80/soap", "", myportfolio[i], "es[i]) != SOAP_OK)
break;
if (soap.error) // an error occurred
soap_print_fault(&soap, stderr);
soap_end(&soap); // clean up all deserialized data
...
这个客户端程序通过调用ns1__getQuote存根例程来为数组中的每个股票代码获得信息。上面的例子给我们示范了使用gSOAP创建一个SOAP客户端时多么容易的事情啊。
2.10 gSOAP文档翻译计划(8.1.2~8.1.3)
8.1.2 关于命名空间
函数ns1__getQuote(上节提到的)中,使用了ns1__作为远程方法的命名空间。使用命名空间是为了防止远程方法名冲突,比方多个服务中使
用同一个远程方法名的情况。
命名空间前缀及命名空间名称同时也被用来验证SOAP信息的内容有效性。存根例程通过命名空间表中的信息来验证服务返回信息。命名空间表在运行时被取出用于解析命名空间绑定,反序列化数据结构,解码并验证服务返回信息。命名空间表不应该包含在gSOAP预编译器所需输入的头文件中。在18.2节中将会对命名空间表做详细介绍。
Delayed Stock Quote服务客户端的命名空间表如下:
struct Namespace namespaces[] =
{ // {"命名前缀", "空间名称"}
{"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/"}, // 必须是第一行
{"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"}, // 必须是第二行
{"xsi", "http://www.w3.org/2001/XMLSchema-instance"}, // 必须是第三行
{"xsd", "http://www.w3.org/2001/XMLSchema"}, // 2001 XML 大纲
{"ns1", "urn:xmethods-delayed-quotes"}, // 通过服务描述获取
{NULL, NULL} // 结束
};
第一行命名空间是SOAP1.1协议默认命名空间。事实上,命名空间表就是用来让程序员可以规定SOAP编码方式,能够用包含命名空间的命名空间前缀来满足指定SOAP服务的命名空间需求的。举例来说,使用前面命名空间表中定义的命名空间前缀ns1,存根例程就可以对getQuote方法的请求进行编码。这个过程由gSOAP预编译器通过在getQuote.h文件中定义的包含前缀ns1的ns1__getQuote函数自动完成。通常,如果要在远程方法名,结构名,类名,字段名等结构或类中使用命名空间前缀,就必须在命名空间表中进行定义。命名空间表将会被存根例程封装,并按下面的形式输出:
...
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns1="urn:xmethods-delayed-quotes"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
...
这个命名空间绑定将被SOAP服务用来验证SOAP请求。
8.1.3 例子
使用命名空间前缀可以解决在不同的服务中使用相同名称的远程方法的问题,看下面的例子:
// Contents of file "getQuote.h":
int ns1__getQuote(char *symbol, float &Result);
int ns2__getQuote(char *ticker, char *"e);
这个例子允许客户端程序使用不同的命名空间以便连接到不同的服务程序执行其中的远程方法。命名空间前缀也可以用在类声明中使用,在XML大纲中区分同名但不同命名空间的SOAP值。例如:
class e__Address // an electronic address
{
char *email;
char *url;
};
class s__Address // a street address
{
char *street;
int number;
char *city;
};
在生成的序列化函数中,使用e__Address的一个实例来表示e命名空间前缀的一个地址元素类型。
<e:Address xsi:type="e:Address">
<email xsi:type="string">me@home</email>
<url xsi:type="string">www.me.com</url>
</e:Address>
用s__Address的一个实例来表示s命名空间前缀的一个地址元素类型。
<s:Address xsi:type="s:Address">
<street xsi:type="string"> Technology Drive </street>
<number xsi:type="int">5</number>
<city xsi:type="string">Softcity</city>
</s:Address>
客户端程序的命名空间表必须有e和s的数据类型定义:
struct Namespace namespaces[] =
{ ...
{"e", "http://www.me.com/schemas/electronic-address"},
{"s", "http://www.me.com/schemas/street-address"},
...
命名空间表必须作为客户端程序的一部分,使客户端程序在运行时可以对数据进行序列化及反序列化。
2.11 gSOAP翻译计划(8.1.4~8.1.6)
8.1.4 如何建立客户端程序代理类
用于C++客户端程序的代理类信息是由gSOAP预编译器自动创建的。为了说明代理类的生成过程,我们在getQuote.h头文件中加入一些信息,以便gSOAP预编译器可以生成代理类。这些信息就类似于WSDL解析器自动生成的头文件中就已经包含的信息。
//"getQuote.h"的内容:
//gSOAP ns1 service name: Quote
//gSOAP ns1 service location: http://services.xmethods.net/soap
//gSOAP ns1 service namespace: urn:xmethods-delayed-quotes
//gSOAP ns1 service style: rpc
//gSOAP ns1 service encoding: encoded
//gSOAP ns1 service method-action: getQuote ""
int ns1__getQuote(char *symbol, float &Result);
前三行指令用于定义代理类的名称,服务地址,命名空间。第四行、第五行指令定义了使用SOAP RPC编码方式。最后一行定义了可选的SOAPAction。当需要SOAPAction时,这行信息将提供给每个远程方法。使用soapcpp2对该头文件进行编译后,将会产生soapQuoteProxy.h文件。它包含下面的内容:
#include "soapH.h"
class Quote
{ public:
struct soap *soap;
const char *endpoint;
Quote() { soap = soap_new(); endpoint = "http://services.xmethods.net/soap"; };
~Quote() { if (soap) { soap_destroy(soap); soap_end(soap); soap_done(soap); free((void*)soap); }};
int getQuote(char *symbol, float &Result) { return soap ? soap_call_ns1__getQuote(soap, endpoint, "", symbol, Result) :
SOAP_EOM; };
};
为了能够在运行时刻对gSOAP环境变量及命名空间进行定制,上述两个变量被定义程全局变量。生成的代理类可以同命名空间表一起包含在客户端程序中,请看下面的例子:
#include "soapQuoteProxy.h" // 获得代理类
#include "Quote.nsmap" // 获得命名空间绑定
int main()
{
Quote q;
float r;
if (q.ns1__getQuote("IBM", r) == SOAP_OK)
std::cout << r << std::endl;
else
soap_print_fault(q.soap, stderr);
return 0;
}
Quote构造函数定义并初始化了一个gSOAP运行环境实例。所有的HTTP及SOAP/XML进程都被隐藏在后台自动执行。如果你需要多个命名空间表或要联合多个客户端,你可以在执行soapcpp2时添加参数-n及-p来生成命名空间表以防止冲突。详细信息请看9.1及18.33节。同时,你可以使用C++代码命名空间来创建一个命名空间限制的代理类,详细信息请看18.32节。
8.1.5 XSD 类型编码
许多SOAP服务需要在SOAP负载中使用XML编码。在gSOAP预编译器中使用的默认编码为SOAP RPC编码。然而,使用XSD类型编码可以改善互操作性。XSD类型在头文件中用typedef定义。举个例子,下面的定义将C/C++类型转换为XSD类型:
// Contents of header file:
...
typedef char *xsd__string; // encode xsd__string value as the xsd:string schema type
typedef char *xsd__anyURI; // encode xsd__anyURI value as the xsd:anyURI schema type
typedef float xsd__float; // encode xsd__float value as the xsd:float schema type
typedef long xsd__int; // encode xsd__int value as the xsd:int schema type
typedef bool xsd__boolean; // encode xsd__boolean value as the xsd:boolean schema type
typedef unsigned long long xsd__positiveInteger; // encode xsd__positiveInteger value as the xsd:positiveInteger schema type
...
这些简单的声明告诉gSOAP预编译器当远程方法参数中使用上述定义的类型时,就把相关的C++类型转当作内建的XSD类型进行编码、解码。同时,使用typedef不需要使用内建C++类型的客户端或服务端程序更改现有代码(但只是当参数为简单的C++类型时,请参看11.2.2节来使用XSD类型表示组合的数据类型)。
8.1.6 例子
重新考虑一席getQuote的例子。现在用XSD类型来重写代码:
// Contents of file "getQuote.h":
typedef char *xsd__string;
typedef float xsd__float;
int ns1__getQuote(xsd__string symbol, xsd__float &Result);
使用预编译器编译这个头文件,将会生成与原来相同的存根例程代码:
int soap_call_ns1__getQuote(struct soap *soap, char *URL, char *action, char *symbol, float &Result);
客户端程序不需要进行任何改动,即可使用XSD类型类编码、解码数据类型信息了。举个例子,当客户端程序调用代理时,代理方法用xsd:string类型产生一个SOAP请求:
...
<SOAP-ENV:Body>
<ns1:getQuote><symbol xsi:type="xsd:string">IBM</symbol>
</ns1:getQuote>
</SOAP-ENV:Body>
...
服务端的返回码为:
...
<soap:Body>
<n:getQuoteResponse xmlns:n="urn:xmethods-delayed-quotes">
<Result xsi:type="xsd:float">41.81</Result>
</n:getQuoteResponse>
</soap:Body