CurrencyConvertor - How use gSOAP and WebServices - Part 2 Doing the first WS client.
This is the second of two articles where I explain building a web service client from wsdl file. With Part One, I explained how to get the class from the wsdl file to be used in VC++ 6. In Part Two, I will show you how to use the classes generated with gSOAP in Part One.
Usually in the file soap*****************SoapProxy.h we can find the class definition of generic WS. In this case, soapCurrencyConvertorSoapProxy.h, I have already included it in Part One.
//soapCurrencyConvertorSoapProxy.h #ifndef soapCurrencyConvertorSoap_H #define soapCurrencyConvertorSoap_H #include "soapH.h" class CurrencyConvertorSoap { public: struct soap *soap; const char *endpoint; CurrencyConvertorSoap() { soap = soap_new(); endpoint = "http://www.webservicex.net/CurrencyConvertor.asmx"; if (soap && !soap->namespaces) { static const struct Namespace namespaces[] = { {"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/*/soap-envelope", NULL}, {"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/*/soap-encoding", NULL}, {"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance", NULL}, {"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema", NULL}, {"ns1", "http://www.webserviceX.NET/", NULL, NULL}, {NULL, NULL, NULL, NULL} }; soap->namespaces = namespaces; } }; virtual ~CurrencyConvertorSoap() { if (soap) { soap_destroy(soap); soap_end(soap); soap_done(soap); soap_del(soap); } }; virtual int __ns1__ConversionRate(_ns1__ConversionRate *ns1__ConversionRate, _ns1__ConversionRateResponse *ns1__ConversionRateResponse) { return soap ? soap_call___ns1__ConversionRate(soap, endpoint, NULL, ns1__ConversionRate, ns1__ConversionRateResponse) : SOAP_EOM; }; }; #endif
In this case, the method is only one and it is __ns1__ConversionRate
.
The class takes two params. The first is the input (the two currencies) and the second is the output (the result).
Before you use the class method of WS you must instantiate the param with soap_instantiate()
, and with soap
, from your CurrencyConvertor
example.
pConversionRate = (_ns1__ConversionRate*) soap_instantiate(m_pCurrencyConvertor->soap, SOAP_TYPE__ns1__ConversionRate,"", "",pConversionRateSize); and pConversionRateResponse = (_ns1__ConversionRateResponse*) soap_instantiate(m_pCurrencyConvertor->soap, SOAP_TYPE__ns1__ConversionRateResponse,/ "","",pConversionRateResponseSize);
This returns an int that is SOAP_OK
, if all is well. (Note: You can find all other definitions for errors in stdsopa2.h)
If this did not return SOAP_OK
you can use soap_print_fault
to get the error string.
In the file Soaph.h you can find methods you can use, like soap_s2ns1__Currency
or soap_ns1__Currency2s
. Just as the name explains, they are used to convert the currency to string and vice versa.
For the test I have develop a new class CMyCurrencyConvertor
that replaces the methods of CurrencyConvertor.
The class CMyCurrencyConvertor
instantiates all the created pointers itself.
I have made only a few public methods and I use them in the dialog.
In the constructor I dynamically allocate at run time the object CurrencyConvertorSoap
.
CMyCurrencyConvertor::CMyCurrencyConvertor() { m_pCurrencyConvertor= new CurrencyConvertorSoap; }
In the destructor I delete the memory allocated for CurrencyConvertorSoap
.
CMyCurrencyConvertor::~CMyCurrencyConvertor() { delete m_pCurrencyConvertor; }
I have two instantiates for the params, InstantiateRate
and InstantiateRateResponse
.
_ns1__ConversionRate * CMyCurrencyConvertor::InstantiateRate() { _ns1__ConversionRate *pConversionRate; size_t *pConversionRateSize = new size_t; *pConversionRateSize = sizeof(_ns1__ConversionRate); pConversionRate = (_ns1__ConversionRate*) soap_instantiate(m_pCurrencyConvertor->soap, SOAP_TYPE__ns1__ConversionRate,"","", pConversionRateSize); delete pConversionRateSize ; return pConversionRate; } _ns1__ConversionRateResponse * CMyCurrencyConvertor::InstantiateRateResponse() { _ns1__ConversionRateResponse *pConversionRateResponse; size_t *pConversionRateResponseSize = new size_t; *pConversionRateResponseSize = sizeof(_ns1__ConversionRateResponse); pConversionRateResponse = (_ns1__ConversionRateResponse*) soap_instantiate(m_pCurrencyConvertor->soap , / SOAP_TYPE__ns1__ConversionRateResponse,"", "",pConversionRateResponseSize); delete pConversionRateResponseSize ; return pConversionRateResponse; }
I take two methods, one takes the input of string Code for currency and the other takes the type of code for currency.
bool CMyCurrencyConvertor::GetConversionRate(ns1__Currency oFromCurrencyCode,
ns1__Currency oToCurrencyCode, double &dRate)
{
int iRet = SOAP_ERR;
_ns1__ConversionRate *pConversionRate;
_ns1__ConversionRateResponse *pConversionRateResponse;
pConversionRate = InstantiateRate();
pConversionRateResponse = InstantiateRateResponse();
pConversionRate->FromCurrency = oFromCurrencyCode;
pConversionRate->ToCurrency = oToCurrencyCode;
iRet = m_pCurrencyConvertor->__ns1__ConversionRate(pConversionRate,
pConversionRateResponse);
switch(iRet)
{
case SOAP_OK :
{
dRate = pConversionRateResponse->ConversionRateResult;
return true;
}
/*
case SOAP_CLI_FAULT
case SOAP_SVR_FAULT
case SOAP_TAG_MISMATCH
case SOAP_TYPE
case SOAP_SYNTAX_ERROR
case SOAP_NO_TAG
case SOAP_IOB
case SOAP_MUSTUNDERSTAND
...
*/
default:
{
Log(GetSoapError());
}
}
return false;
}
I create the two param pointers and instantiate it. I set the two currency code and call the ConversionRate
.
If all works fine, I return the conversion, otherwise I return an error.
// Conversion from String code
bool CMyCurrencyConvertor::GetConversionRate(CString sFromCurrencyCode,
CString sToCurrencyCode, double &dRate)
{
ns1__Currency oFromCurrencyCode;
ns1__Currency oToCurrencyCode;
int iRetFromCurrencyCode = soap_s2ns1__Currency
(m_pCurrencyConvertor->soap,sFromCurrencyCode,
&oFromCurrencyCode);
int iRetToCurrencyCode = soap_s2ns1__Currency
(m_pCurrencyConvertor->soap,sToCurrencyCode,
&oToCurrencyCode);
return GetConversionRate(oFromCurrencyCode,oToCurrencyCode,dRate);
}
soap_s2ns1__Currency
.
I add another method for retrieving the error from soap to string.
CString CMyCurrencyConvertor::GetSoapError() { struct soap *pSoap = m_pCurrencyConvertor->soap; CString sError; if (soap_check_state(pSoap )) sError.Format("Error: soap struct not initialized/n"); else { if (pSoap->error) { const char *pFaultCode, *pFaultSubCode = NULL, *pFalutString, **iFaultCode; iFaultCode = soap_faultcode(pSoap ); if (!*iFaultCode) soap_set_fault(pSoap ); pFaultCode = *iFaultCode; if (pSoap ->version == 2) pFaultSubCode = *soap_faultsubcode(pSoap ); pFalutString = *soap_faultstring(pSoap ); iFaultCode = soap_faultdetail(pSoap ); sError.Format("%s%d fault: %s [%s]/"%s/"Detail: %s", pSoap->version ? "SOAP 1." :/ "Error ", pSoap->version ? (int)pSoap->version : pSoap->error, pFaultCode, / pFaultSubCode ? pFaultSubCode : "no subcode", pFalutString ? pFalutString : "[no reason]", / iFaultCode && *iFaultCode ? *iFaultCode : "[no detail]"); } } return sError; }
This is a useful function to convert currency to printable string and vice versa:
bool CMyCurrencyConvertor::String2Currency(CString sCurrencyCode,
ns1__Currency &oCurrencyCode)
{
int iRet = soap_s2ns1__Currency(m_pCurrencyConvertor->soap,
sCurrencyCode, &oCurrencyCode);
switch(iRet)
{
case SOAP_OK :
{
return true;
}
/*
case SOAP_CLI_FAULT
case SOAP_SVR_FAULT
case SOAP_TAG_MISMATCH
case SOAP_TYPE
case SOAP_SYNTAX_ERROR
case SOAP_NO_TAG
case SOAP_IOB
case SOAP_MUSTUNDERSTAND
...
*/
default:
{
Log(GetSoapError());
}
}
return false;
}
bool CMyCurrencyConvertor::Currency2String(ns1__Currency oCurrencyCode,
CString &sCurrencyCode)
{
const char* sName =
soap_ns1__Currency2s(m_pCurrencyConvertor->soap,oCurrencyCode);
if (sName != NULL)
{
CString sTMP(sName);
sCurrencyCode = sTMP;
return true;
}
else
Log(GetSoapError());
return false;
}
I created a dialog project and I use the methods I describe again.
I take a cycle for retrieval, and display the currency code, like this:
CString sCodeName; for (int iDx=ns1__Currency__AFA; iDx <;ns1__Currency__TRY+1; iDx++) { m_oMyCurrencyConvertor.Currency2String((ns1__Currency) iDx, sCodeName); m_cboFromCurrency.AddString(sCodeName); m_cboToCurrency.AddString(sCodeName); }
When you push a button I retrieve the currency codes selected and use GetConversionRate
to call WS.
double dRate;
CString sFromCurrency,sToCurrency;
m_cboFromCurrency.GetWindowText(sFromCurrency);
m_cboToCurrency.GetWindowText(sToCurrency);
if (m_oMyCurrencyConvertor.GetConversionRate(sFromCurrency,
sToCurrency,dRate))
{
int iColumnCount = m_lstConversionRate.GetItemCount();
CString sRate;
sRate.Format("%f",dRate);
m_lstConversionRate.InsertItem(iColumnCount,sFromCurrency);
m_lstConversionRate.SetItemText(iColumnCount,iIDX_TO,sToCurrency);
m_lstConversionRate.SetItemText(iColumnCount,iIDX_CONVERSION,sRate);
}
Use it however you wish.