Featured Post

Trie implementation in C

Create a SOAP web service client in C++

What is SOAP?

SOAP(Simple Object Access Protocol) is a great way to exchange information over the network. Normally it is used with application protocols like HTTP, SMTP etc. The envelope containing the information is XML based.

It provides the basic messaging infrastructure for web services. 

An example SOAP request looks like :


 
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Header>
    <ns1:RequestHeader
         soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next"
         soapenv:mustUnderstand="0"
         xmlns:ns1="https://www.google.com/apis/ads/publisher/v201403">
      <ns1:networkCode>123456</ns1:networkCode>
      <ns1:applicationName>DfpApi-Java-2.1.0-dfp_test</ns1:applicationName>
    </ns1:RequestHeader>
  </soapenv:Header>
  <soapenv:Body>
    <getAdUnitsByStatement xmlns="https://www.google.com/apis/ads/publisher/v201403">
      <filterStatement>
        <query>WHERE parentId IS NULL LIMIT 500</query>
      </filterStatement>
    </getAdUnitsByStatement>
  </soapenv:Body>
</soapenv:Envelope>

The corresponding response would look like :
 
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <ResponseHeader xmlns="https://www.google.com/apis/ads/publisher/v201403">
      <requestId>xxxxxxxxxxxxxxxxxxxx</requestId>
      <responseTime>1063</responseTime>
    </ResponseHeader>
  </soap:Header>
  <soap:Body>
    <getAdUnitsByStatementResponse xmlns="https://www.google.com/apis/ads/publisher/v201403">
      <rval>
        <totalResultSetSize>1</totalResultSetSize>
        <startIndex>0</startIndex>
        <results>
          <id>2372</id>
          <name>RootAdUnit</name>
          <description></description>
          <targetWindow>TOP</targetWindow>
          <status>ACTIVE</status>
          <adUnitCode>1002372</adUnitCode>
          <inheritedAdSenseSettings>
            <value>
              <adSenseEnabled>true</adSenseEnabled>
              <borderColor>FFFFFF</borderColor>
              <titleColor>0000FF</titleColor>
              <backgroundColor>FFFFFF</backgroundColor>
              <textColor>000000</textColor>
              <urlColor>008000</urlColor>
              <adType>TEXT_AND_IMAGE</adType>
              <borderStyle>DEFAULT</borderStyle>
              <fontFamily>DEFAULT</fontFamily>
              <fontSize>DEFAULT</fontSize>
            </value>
          </inheritedAdSenseSettings>
        </results>
      </rval>
    </getAdUnitsByStatementResponse>
  </soap:Body>
</soap:Envelope>
Courtesy : https://developers.google.com/doubleclick-publishers/docs/soap_xml

The above is an example of Google's web service which provides Ad Units to the requester based on some filters.

Web Services provide an easy platform independent way to exchange information over the network.
The following picture shows the basic flow on how the Service Provider caters the request of the consumer.

                                                                           Source: http://www.service-architecture.com
Following is an implementation of a SOAP client in C++ which calls the Web Service to get the stock quotes based on a symbol(company ticker).

The example uses gSoap library for SOAP encoding/decoding and Xerces library to extract the data from the SOAP response (XML).

I have used the web service URL http://www.webservicex.net/stockquote.asmx?WSDL

Resources:

  1. gSOAP library (2.8.18) http://sourceforge.net/projects/gsoap2/files/
  2. Xerces library (3.1.1) http://xerces.apache.org/mirrors.cgi#binary
Setup for Visual Studio 2005:
  1. Unzip gSOAP library at any location (Lets say C:\tools , so the location will be C:\gsoap-2.8)
  2. Download only the binary distribution of xerces xerces-c-3.1.1-x86-windows-vc-8.0.zip
  3. Unzip the zip file at C:\  so that location becomes C:\xerces-c-3.1.1-x86-windows-vc-8.0
  4. Create an empty Win32 Console Application project.
  5. Add theses paths to the Additional Include Directories :
    "C:\xerces-c-3.1.1-x86-windows-vc-8.0\include";"C:\gsoap-2.8\gsoap\";"C:\gsoap-2.8\gsoap\import"

  6. Add these paths to Additional Library Directories :
    "C:\xerces-c-3.1.1-x86-windows-vc-8.0\lib"

  7. Add these libs to the dependency list:


    Notice that since we are using a Debug configuration here, we added xerces-c_3D.lib. For Release configuration, use xerces-c_3.lib
  8. Add this location to your environment Path variable C:\xerces-c-3.1.1-x86-windows-vc-8.0\bin or copy the xerces dlls (xerces-c_3_1.dll and xerces-c_3_1D.dll) into the output path of the project(i.e. same path where the exe of the project will be created). These will be present at the location C:\xerces-c-3.1.1-x86-windows-vc-8.0\bin
  9. Open a command prompt and go to gSOAP win32 bin directory and run following commands :

    wsdl2h.exe -o quote.h http://www.webservicex.net/stockquote.asmx?WSDL


    This will generate quote.h which contains class definitions for the web service.
    soapcpp2.exe /IC:\tools\gsoap_2.8.18\gsoap-2.8\gsoap\import quote.h
    This generates following files :

    StockQuoteSoap.GetQuote.req.xml
    StockQuoteSoap.GetQuote.res.xml
    StockQuoteSoap.nsmap
    soapC.cpp
    soapClient.cpp
    soapClientLib.cpp
    soapH.h
    soapServer.cpp
    soapServerLib.cpp
    soapStub.h
  10. Now add these generated files to the Visual Studio project created earlier :
    soapH.h , 
    soapC.cpp, soapClient.cpp, soapStub.h, quote.h

    Also these add two additional files from C:\tools\gsoap_2.8.18\gsoap-2.8\gsoap to your project:
    stdsoap2.cpp, stdsoap2.h
  11. Now its time to create some files on our own :).  Create these files in the project and copy the contents from the code below :

    quote.cpp, parser.hpp, stock.hpp
  12. SOAP Web Service client implementation(c++)

    
    //quote.cpp
    #include "soapH.h"    // include the generated proxy
    #include <xercesc/sax/HandlerBase.hpp>
    #include <xercesc/util/XMLString.hpp>
    #include <xercesc/framework/MemBufInputSource.hpp>
    #include <xercesc/util/OutOfMemoryException.hpp>
    #include <xercesc/dom/DOM.hpp>
    #include <xercesc/dom/DOMDocument.hpp>
    #include <xercesc/dom/DOMDocumentType.hpp>
    #include <xercesc/dom/DOMElement.hpp>
    #include <xercesc/dom/DOMImplementation.hpp>
    #include <xercesc/dom/DOMImplementationLS.hpp>
    #include <xercesc/dom/DOMNodeIterator.hpp>
    #include <xercesc/dom/DOMNodeList.hpp>
    #include <xercesc/dom/DOMText.hpp>
    #include <xercesc/parsers/XercesDOMParser.hpp>
    #include <xercesc/util/XMLUni.hpp>
    #include "parser.hpp"
    
    XERCES_CPP_NAMESPACE_USE
    
    GetXml::GetXml()
    {
     try
     {
      XMLPlatformUtils::Initialize();  // Initialize Xerces infrastructure
     }
     catch( XMLException& e )
     {
      char* message = XMLString::transcode( e.getMessage() );
      std::cout << "XML toolkit initialization error: " << message << std::endl;
      XMLString::release( &message );
     }
    
     // Tags and attributes used in XML file.
     // Can't call transcode till after Xerces Initialize()
     TAG_root  = XMLString::transcode("StockQuotes");
     TAG_Stock = XMLString::transcode("Stock");
     TAG_Symbol = XMLString::transcode("Symbol");
     TAG_Last = XMLString::transcode("Last");
     TAG_Date = XMLString::transcode("Date");
     TAG_Time = XMLString::transcode("Time");
     TAG_Change = XMLString::transcode("Change");
     TAG_Open = XMLString::transcode("Open");
     TAG_High = XMLString::transcode("High");
     TAG_Low = XMLString::transcode("Low");
     TAG_Volume = XMLString::transcode("Volume");
     TAG_MktCap = XMLString::transcode("MktCap");
     TAG_PrevClose = XMLString::transcode("PreviousClose");
     TAG_PercentChange = XMLString::transcode("PercentageChange");
     TAG_AnnRange = XMLString::transcode("AnnRange");
     TAG_Earns = XMLString::transcode("Earns");
     TAG_PE = XMLString::transcode("P-E");
     TAG_Name = XMLString::transcode("Name");
     
     m_XmlParser = new XercesDOMParser;
     m_Stock = new Stock;
    }
    
    GetXml::~GetXml()
    {
     // Free memory
     delete m_XmlParser;
     delete m_Stock;
    
     try
     {
      XMLString::release( &TAG_root );
     }
     catch( ... )
     {
      std::cout << "Unknown exception encountered in Destructor" << std::endl;
     }
    
     // Terminate Xerces
     try
     {
      XMLPlatformUtils::Terminate();  // Terminate after release of memory
     }
     catch( xercesc::XMLException& e )
     {
      char* message = xercesc::XMLString::transcode( e.getMessage() );
    
      std::cout << "XML toolkit teardown error: " << message << std::endl;
      XMLString::release( &message );
     }
    }
    
    void GetXml::readXml(std::string& xmlStr)
    throw( std::runtime_error )
    {
     // Configure DOM parser.
     m_XmlParser->setValidationScheme( XercesDOMParser::Val_Never );
     m_XmlParser->setDoNamespaces( false );
     m_XmlParser->setDoSchema( false );
     m_XmlParser->setLoadExternalDTD( false );
    
     try
     {
      xercesc_3_1::MemBufInputSource xmlBuf((const XMLByte*)xmlStr.c_str(), xmlStr.size(),
       "xmlBuf (in memory)");
      m_XmlParser->parse( xmlBuf );
    
      xercesc_3_1::DOMDocument* xmlDoc = m_XmlParser->getDocument();
    
      DOMElement* elementRoot = xmlDoc->getDocumentElement();
      if( !elementRoot ) throw(std::runtime_error( "Empty XML document" ));
    
      DOMNodeList*      children = elementRoot->getChildNodes();
      const  XMLSize_t nodeCount = children->getLength();
    
      // For all nodes, children of "StockQuotes" in the XML tree.
    
      for( XMLSize_t xx = 0; xx < nodeCount; ++xx )
      {
       DOMNode* currentNode = children->item(xx);
       if( currentNode->getNodeType() &&  // true is not NULL
        currentNode->getNodeType() == DOMNode::ELEMENT_NODE ) // is element 
       {
        // Found node which is an Element. Re-cast node as element
        DOMElement* currentElement
         = dynamic_cast< xercesc::DOMElement* >( currentNode );
        if( XMLString::equals(currentElement->getTagName(), TAG_Stock))
        {
         DOMNodeList*      children1 = currentElement->getChildNodes();
         const  XMLSize_t nodeCount1 = children1->getLength();
         for( XMLSize_t yy = 0; yy < nodeCount1; ++yy )
         {
          DOMNode* currentNode1 = children1->item(yy);
          if( currentNode1->getNodeType() &&  // true is not NULL
           currentNode1->getNodeType() == DOMNode::ELEMENT_NODE ) // is element 
          {
           // Found node which is an Element. Re-cast node as element
           DOMElement* currentElement1
            = dynamic_cast< xercesc::DOMElement* >( currentNode1 );
           std::cout<<XMLString::transcode(currentElement1->getTagName())<<": "
            <<XMLString::transcode(currentElement1->getTextContent())<<std::endl;
    
           std::string sym(XMLString::transcode(currentElement1->getTextContent()));
    
           if(XMLString::equals(currentElement1->getTagName(), TAG_Symbol))
           {
            m_Stock->SetSymbol(sym);
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_Last))
           {
            m_Stock->SetLast(sym);
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_Date))
           {
            m_Stock->SetDate(sym);
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_Time))
           {
            m_Stock->SetTime(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_Change))
           {
            m_Stock->SetChange(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_Open))
           {
            m_Stock->SetOpen(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_High))
           {
            m_Stock->SetHigh(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_Low))
           {
            m_Stock->SetLow(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_Volume))
           {
            m_Stock->SetVolume(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_MktCap))
           {
            m_Stock->SetMktCap(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_PrevClose))
           {
            m_Stock->SetPrevClose(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_PercentChange))
           {
            m_Stock->SetPercentChange(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_AnnRange))
           {
            m_Stock->SetAnnRange(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_Earns))
           {
            m_Stock->SetEarns(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_PE))
           {
            m_Stock->SetPE(sym);
    
           }
           else if(XMLString::equals(currentElement1->getTagName(), TAG_Name))
           {
            m_Stock->SetName(sym);
           }
          }
         }
        }
       }
      }
    
      std::cout<<m_Stock->ToString();
     }
     catch( xercesc::XMLException& e )
     {
      char* message = xercesc::XMLString::transcode( e.getMessage() );
      std::ostringstream errBuf;
      errBuf << "Error parsing file: " << message << std::flush;
      XMLString::release( &message );
     }
    }
    
    int main(int argc, char** argv){
     struct soap *soap = soap_new(); 
     struct _ns1__GetQuote sym;
     struct _ns1__GetQuoteResponse quote;
     GetXml xml;
     std::string response="";
     std::string str(argv[1]);
     sym.symbol = &str;
    
     soap_init(soap);
    
     //Call the web service
     if (soap_call___ns1__GetQuote(soap, NULL, NULL, &sym, quote) == SOAP_OK) {
      //std::cout<<"Symbol: "<<*(sym.symbol)<<std::endl<<" Quote: "<<*(quote.GetQuoteResult)<<std::endl;
      response = *(quote.GetQuoteResult);
      free (quote.GetQuoteResult);
     }
     else {
      std::cout<<"Error in execution of GetQuote:: "<<soap->buf<<std::endl;
      return(0);
     }
    
     //Parse the SOAP response 
     xml.readXml(response);
     return 0;
    }
    
    // Copied from StockQuoteSoap.nsmap file
    SOAP_NMAC struct Namespace namespaces[] =
    {
     {"SOAP-ENV", "http://www.w3.org/2003/05/soap-envelope", "http://schemas.xmlsoap.org/soap/envelope/", NULL},
     {"SOAP-ENC", "http://www.w3.org/2003/05/soap-encoding", "http://schemas.xmlsoap.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}
    };
    
    



    
    //stock.hpp
    #include <sstream>
    class Stock
    {
     std::string symbol_;
     std::string last_;
     std::string date_;
     std::string time_;
     std::string change_;
     std::string open_;
     std::string high_;
     std::string low_;
     std::string volume_;
     std::string mktcap_;
     std::string previousclose_;
     std::string percentagechange_;
     std::string annrange_;
     std::string earns_;
     std::string p_e_;
     std::string name_;
    
    public:
     Stock() {}
     Stock(std::string symbol) {symbol_ = symbol;}
    
     void SetSymbol(std::string symbol) {symbol_ = symbol;}
     void SetLast(std::string last) {last_ = last;}
     void SetDate(std::string date) {date_ = date;}
     void SetTime(std::string time) {time_ = time;}
     void SetChange(std::string change) {change_ = change;}
     void SetOpen(std::string open) {open_ = open;}
     void SetHigh(std::string high) {high_ = high;}
     void SetLow(std::string low) {low_ = low;}
     void SetVolume(std::string volume) {volume_ = volume;}
     void SetMktCap(std::string mktcap) {mktcap_ = mktcap;}
     void SetPrevClose(std::string previousclose) {previousclose_ = previousclose;}
     void SetPercentChange(std::string percentagechange) {percentagechange_ = percentagechange;}
     void SetAnnRange(std::string annrange) {annrange_ = annrange;}
     void SetEarns(std::string earns) {earns_ = earns;}
     void SetPE(std::string p_e) {p_e_ = p_e;}
     void SetName(std::string name) {name_ = name;}
    
     //ToString function overridden to display in short format  
     std::string ToString() const
     {
      std::stringstream sstr;
      sstr << "\'" << symbol_ << "\' Open: " << open_
       << ", Last: " << last_;
      return sstr.str();
     }
    };
    
    



    
    //parser.hpp
    #include "stock.hpp"
    class GetXml
    {
    public:
     GetXml();
     ~GetXml();
    
     //Function to read the XML string
     void readXml(std::string&) throw(std::runtime_error);
    
    
    private:
     xercesc_3_1::XercesDOMParser *m_XmlParser; //DOM Parser pointer
     Stock *m_Stock;        //To save the returned data
    
     XMLCh* TAG_root;
     XMLCh* TAG_Stock;
     XMLCh* TAG_Symbol;
     XMLCh* TAG_Last;
     XMLCh* TAG_Date;
     XMLCh* TAG_Time;
     XMLCh* TAG_Change;
     XMLCh* TAG_Open;
     XMLCh* TAG_High;
     XMLCh* TAG_Low;
     XMLCh* TAG_Volume;
     XMLCh* TAG_MktCap;
     XMLCh* TAG_PrevClose;
     XMLCh* TAG_PercentChange;
     XMLCh* TAG_AnnRange;
     XMLCh* TAG_Earns;
     XMLCh* TAG_PE;
     XMLCh* TAG_Name;
    };
    
    


  13. Now the project is ready to compile. Use F7 to compile the solution.
  14. After the binary is generated, run it like this :
    <binary-name>.exe<space><Ticker-Name>

    e.g. If project name was soap
    soap.exe GOOG
  15. This program is specific to the Stock Quote web service provided by http://www.webservicex.net/ . This program can be modified to use any web service. Depending upon the expected SOAP response, the result can be parsed accordingly. Then following files will be modified :

    stock.hpp - Class which stores the data from SOAP response
    parser.hpp - Class which parses the XML result from SOAP response

Comments

  1. Web services are client and server applications that communicate over the World Wide Web’s (WWW) HyperText Transfer Protocol (HTTP). Web services provide a standard means of inter operating between software applications running on a variety of platforms and frameworks. Web Design Services

    ReplyDelete
  2. The article provided by you is very nice and it is very helpful to know the more information.keep update with your blogsRuby on rails Online Course

    ReplyDelete

Post a Comment

Please post your valuable suggestions