The gSOAP compiler tools provide a SOAP/XML-to-C/C++ language binding to ease the development of SOAP/XML Web services and client application in C and/or C++. Most toolkits for C++ Web services adopt a SOAP-centric view and offer APIs for C++ that require the use of class libraries for SOAP-specific data structures. This often forces a user to adapt the application logic to these libraries. In contrast, gSOAP provides a C/C++ transparent SOAP API through the use of compiler technology that hides irrelevant SOAP-specific details from the user. The gSOAP stub and skeleton compiler automatically maps native and user-defined C and C++ data types to semantically equivalent XML data types and vice-versa. As a result, full SOAP interoperability is achieved with a simple API relieving the user from the burden of SOAP details, thus enabling him or her to concentrate on the application-essential logic. The compiler enables the integration of (legacy) C/C++ and Fortran codes (through a Fortran to C interface), embedded systems, and real-time software in SOAP applications that share computational resources and information with other SOAP applications, possibly across different platforms, language environments, and disparate organizations located behind firewalls.
gSOAP minimizes application adaptation for building Web Services. The gSOAP compiler generates SOAP marshalling routines that (de)serialize application-specific C/C++ data structures. gSOAP includes a WSDL generator to generate Web service descriptions for your Web services. The gSOAP WSDL parser and importer "closes the circle" in that it enables client development without the need for users to analyze Web service details to implement a client or a server.
Some of the highlights of gSOAP are:
The typographical conventions used by this document are:
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC-2119.
To comply with WS-I Basic Profile 1.0a, gSOAP 2.5 and higher adopts SOAP RPC literal by default. There is no need for concern, because the WSDL parser wsdl2h automatically takes care of the differences when you provide a WSDL document, because SOAP RPC encoding, literal, and document style are supported. A new soapcpp2 compiler option was added -e for backward compatibility with gSOAP 2.4 and earlier to adopt SOAP RPC encoding by default in case you want to develop a service that uses SOAP encoding. You can also use the gSOAP compiler directives to specify SOAP encoding for individual operarations, when desired.
You should read this section only if you are upgrading from gSOAP 2.1 to 2.2 and later.
Run-time options and flags have been changed to enable separate recv/send settings for transport, content encodings, and mappings. The flags are divided into four classes: transport (IO), content encoding (ENC), XML marshalling (XML), and C/C++ data mapping (C). The old-style flags soap_disable_X and soap_enable_X, where X is a particular feature, are deprecated. See Section 9.12 for more details.
You should read this section only if you are upgrading from gSOAP 1.X to 2.X.
gSOAP versions 2.0 and later have been rewritten based on versions 1.X.
gSOAP 2.0 and later is thread-safe, while 1.X is not.
All files in the gSOAP 2.X distribution are renamed to avoid confusion with gSOAP version 1.X files:
|
The gSOAP runtime environment is stored in a struct soap type. A struct was chosen to support application development in
C without the need for a separate gSOAP implementation. An object-oriented approach with a class for the gSOAP runtime environment would have prohibited the implementation of pure C applications.
Before a client can invoke remote methods or before a service can accept requests, a runtime environment need to be allocated and
initialized.
Three new functions are added to gSOAP 2.X:
|
|
int main() { struct soap soap; ... soap_init(&soap); // initialize runtime environment ... soap_call_ns__method1(&soap, ...); // make a remote call ... soap_call_ns__method2(&soap, ...); // make another remote call ... soap_end(&soap); // clean up ... } |
|
int main() { struct soap *soap; ... soap = soap_new(); // allocate and initialize runtime environment if (!soap) // couldn't allocate: stop ... soap_call_ns__method1(soap, ...); // make a remote call ... soap_call_ns__method2(soap, ...); // make another remote call ... soap_end(soap); // clean up ... free(soap); // deallocate runtime environment } |
|
int main() { struct soap soap; soap_init(&soap); soap_serve(&soap); } |
|
int main() { soap_serve(soap_new()); } |
A service can use multi-threading to handle requests while running some other code that invokes remote methods:
|
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, ...); // make a remote call ... soap_end(&soap2); ... pthread_join(tid, NULL); // wait for thread to terminate soap_end(&soap1); // release its data } |
Section 8.2.4 presents a multi-threaded stand-alone Web Service that handles multiple SOAP requests by spawning a thread for each request.
gSOAP interoperability has been verified with the following SOAP implementations and toolkits:
To start building Web services applications with gSOAP, you need:
Although gSOAP is available in binary format for several platforms, the code generated by the gSOAP stub and skeleton compiler and the gSOAP runtime codes are equivalent. This means that the generated codes can be transferred to other platforms and compiled.
The platform-independent release of gSOAP requires you to build the 'soapcpp2' compiler and 'wsdl2h' parser. The minimal requirements for your platform to build these executables is:
The gSOAP packages contain numerous examples in the 'samples' directory. Run 'make' to build the example applications. The examples are also meant to demonstrate different features of gSOAP. The simplest examples are the one-liners (samples/oneliners). Indeed, you can write a one-line Web service (requires CGI). A streaming DIME attachment server and client application demonstrate efficient file exchanges (samples/dime). An SSL-secure Web server application demonstrates the generation of dynamic content for Web browsing and Web services functionality at the same time. And much more.
This user guide offers a quick way to get started with gSOAP. This section requires a basic understanding of the SOAP 1.1 protocol and some familiarity with C and/or C++. In principle, SOAP clients and SOAP Web services can be developed in C and C++ with the gSOAP compiler without a detailed understanding of the SOAP protocol when gSOAP client-server applications are built as an ensamble and only communicate within this group (i.e. meaning that you don't have to worry about interoperability with other SOAP implementations). This section is intended to illustrate the implementation of gSOAP Web services and clients that connect to and interoperate with other SOAP implementations such as Apache Axis, SOAP::Lite, and .NET. This requires some details of the SOAP and WSDL protocols to be understood.
In general, the implementation of a SOAP client application requires a stub routine for each remote method that the client application needs to invoke. The primary stub's responsibility is to marshall the parameter data, send the request with the parameters to the designated SOAP service over the wire, to wait for the response, and to demarshall the parameter data of the response when it arrives. The client application invokes the stub routine for a remote method as if it would invoke a local method. To write a stub routine in C or C++ by hand is a tedious task, especially if the input and/or output parameters of a remote method contain elaborate data structures such as records, arrays, and graphs. Fortunately, the gSOAP 'wsdl2h' WSDL parser and 'soapcpp2' stub and skeleton compiler automate the development of Web service client and server applications.
The gSOAP stub and skeleton compiler is a preprocessor that generates the necessary C++ sources to build SOAP C++ clients. The input to the gSOAP stub and skeleton compiler consists of a standard C/C++ header file. The header file can be generated from a WSDL (Web Service Description Language) documentation of a service with the gSOAP WSDL parser.
Consider the following command (entered at the command prompt):
| $ wsdl2h -o quote.h http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl |
To generate a header file to develop a pure C client application, issue the command:
Consider the following command (entered at the command prompt):
| $ wsdl2h -c -o quote.h http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl |
The quote.h header file is then processed by the gSOAP compiler to generate the stubs to develop client applications (and skeletons to develop a service).
The SOAP service methods are specified in the header file as function prototypes. Stub routines in C/C++ source form are automatically generated by the gSOAP compiler for these function prototypes of remote methods. The resulting stub routines allow C and C++ client applications to seamlessly interact with existing SOAP Web services.
The gSOAP stub and skeleton compiler also generates skeleton routines for each of the remote methods specified in the header file. The skeleton routines can be readily used to implement one or more of the remote methods in a new SOAP Web service. These skeleton routines are not used for building SOAP clients in C++, although they can be used to build mixed SOAP client/server applications (peer applications).
The input and output parameters of a SOAP service method may be simple data types or compound data types, either generated by the WSDL parser or specified by hand. The gSOAP stub and skeleton compiler automatically generates serializers and deserializers for the data types to enable the generated stub routines to encode and decode the contents of the parameters of the remote methods in XML.
The getQuote remote method of XMethods Delayed Stock Quote service (defined in the quote.h file obtained with the 'wsdl2h' WSDL parser)
provides a delayed stock quote for a given ticker name.
The WSDL description of the XMethods Delayed Stock Quote service provides the following details:
|
|
//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); |
The Delayed Stock Quote service description requires that the input parameter of the getQuote remote method is a symbol parameter of type string. The description also indicates that the Result output parameter is a floating point number that represents the current unit price of the stock in dollars. The gSOAP compiler uses the convention the last parameter of the function prototype must be the output parameter of the remote method, which is required to be passed by reference using the reference operator (&) or by using a pointer type. All other parameters except the last are input parameters of the remote method, which are required to be passed by value or passed using a pointer to a value (by reference is not allowed). The function prototype associated with a remote method is required to return an int, whose value indicates to the caller whether the connection to a SOAP Web service was successful or resulted in an exception, see Section 10.2 for the error codes.
The use of the namespace prefix ns1__ in the remote method name in the function prototype declaration is discussed in detail in 8.1.2. Basically, a namespace prefix is distinguished by a pair of underscores in the function name, as in ns1__getQuote where ns1 is the namespace prefix and getQuote is the remote method name. (A single underscore in an identifier name will be translated into a dash in XML, because dashes are more frequently used in XML compared to underscores, see Section 10.3.)
The gSOAP compiler is invoked from the command line with:
| soapcpp2 getQuote.h |
| int soap_call_ns1__getQuote(struct soap *soap, char *URL, char *action, char *symbol, float &Result); |
Note that the parameters of the soap_call_ns1__getQuote function are identical to the ns1__getQuote function prototype with three additional input parameters: soap must be a valid pointer to a gSOAP runtime environment, URL is the SOAP Web service endpoint URL passed as a string, and action is a string that denotes the SOAP action required by the Web service. Note that the XMethods Delayed Stock Quote service endpoint URL is http://66.28.98.121:9090/soap and the SOAP action required is "" (two quotes). You can change the endpoint and action dynamically. The endpoint and action are the second and third parameters of the soap_call_ns1__getQuote. When NULL, the values specified in the header file will be used.
The following example mixed C/C++ client program invokes the stub to retrieve the latest IBM stock quote from the XMethods Delayed Stock
Quote service:
|
#include "soapH.h" // obtain the generated stub #include "net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding.nsmap" // obtain the namespace mapping table int main() { struct soap soap; // gSOAP runtime environment float quote; soap_init(&soap); // initialize runtime environment (only once) 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); // display the SOAP fault message on the stderr stream soap_destroy(&soap); // delete deserialized class instances (for C++ only) soap_end(&soap); // remove deserialized data and clean up soap_done(&soap); // detach the gSOAP environment return 0; } |
The gSOAP compiler also generates a proxy class for C++ client applications. This generated proxy class can be included into a client application together with the generated namespace table as shown in this example:
|
#include "soapnet_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBindingProxy.h" // get proxy #include "net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding.nsmap" // obtain the namespace mapping table int main() { net q; // "net" is the proxy class with a name that is the short name of the service 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 ns1 service name: net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding |
The following functions can be used to explicitly setup a gSOAP runtime environment (struct soap):
|
When the example client application is invoked, the SOAP request is performed by the stub routine soap_call_ns1__getQuote, which
generates the following SOAP RPC request message:
|
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> |
|
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> |
A client program can invoke a remote method at any time and multiple times if necessary. Consider for example:
|
... 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 ... |
This example demonstrated how easy it is to build a SOAP client with gSOAP once the details of a Web service are available in the form of a WSDL document.
The declaration of the ns1__getQuote function prototype (discussed in the previous section) uses the namespace prefix ns1__ of the remote method namespace, which is distinguished by a pair of underscores in the function name to separate the namespace prefix from the remote method name. The purpose of a namespace prefix is to associate a remote method name with a service in order to prevent naming conflicts, e.g. to distinguish identical remote method names used by different services.
Note that the XML response of the XMethods Delayed Stock Quote service example uses the namespace prefix n which is bound to the namespace name urn:xmethods-delayed-quotes through the xmlns:n="urn:xmethods-delayed-quotes binding. The use of namespace prefixes and namespace names is also required to enable SOAP applications to validate the content of SOAP messages. The namespace name in the service response is verified by the stub routine by using the information supplied in a namespace mapping table that is required to be part of gSOAP client and service application codes. The table is accessed at run time to resolve namespace bindings, both by the generated stub's data structure serializer for encoding the client request and by the generated stub's data structure deserializer to decode and validate the service response. The namespace mapping table should not be part of the header file input to the gSOAP stub and skeleton compiler. Service details including namespace bindings may be provided with gSOAP directives in a header file, see Section 17.2.
The namespace mapping table for the Delayed Stock Quote client is:
|
struct Namespace namespaces[] = { // {"ns-prefix", "ns-name"} {"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/"}, // MUST be first {"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"}, // MUST be second {"xsi", "http://www.w3.org/2001/XMLSchema-instance"}, // MUST be third {"xsd", "http://www.w3.org/2001/XMLSchema"}, // 2001 XML schema {"ns1", "urn:xmethods-delayed-quotes"}, // given by the service description {NULL, NULL} // end of table }; |
The namespace mapping table will be output as part of the SOAP Envelope by the stub routine. For example:
|
... <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/"> ... |
The incorporation of namespace prefixes into C++ identifier names is necessary to distinguish remote methods that
share the same name but are provided by separate Web services and/or organizations. Consider for example:
|
// Contents of file "getQuote.h": int ns1__getQuote(char *symbol, float &Result); int ns2__getQuote(char *ticker, char *"e); |
This example enables a client program to connect to a (hypothetical) Stock Quote service with remote methods that can only be distinguished by their namespaces. Consequently, two different namespace prefixes had to be used as part of the remote method names.
The namespace prefix convention can also be applied to class declarations that contain SOAP compound values
that share the same name but have different namespaces that refer to different XML schemas. For example:
|
class e__Address // an electronic address { char *email; char *url; }; class s__Address // a street address { char *street; int number; char *city; }; |
An instance of e__Address is encoded by the generated serializer for this type as an Address element with namespace prefix 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 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> |
|
struct Namespace namespaces[] = { ... {"e", "http://www.me.com/schemas/electronic-address"}, {"s", "http://www.me.com/schemas/street-address"}, ... |
Proxy classes for C++ client applications are automatically generated by the gSOAP compiler.
To illustrate the generation of a proxy class, the getQuote.h header file example of the previous section is augmented with the appropriate directives to enable the gSOAP compiler to
generate the proxy class. Similar directives are included in the header file by the WSDL importer.
|
// Content of file "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); |
|
#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; }; }; |
This generated proxy class can be included into a client application together with the generated namespace table as shown in this example:
|
#include "soapQuoteProxy.h" // get proxy #include "Quote.nsmap" // get namespace bindings 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; } |
You can use soapcpp2 compiler option -n together with -p to create a local namespaces table to avoid link conflicts when you need multiple namespace tables or need to combine multiple clients, see also Sections 9.1 and 17.32, and you can use a C++ code namespace to create a namespace qualified proxy class, see Section 17.31.
Many SOAP services require the explicit use of XML schema types in the SOAP payload. The default encoding, which is also adopted
by the gSOAP compiler, assumes SOAP RPC encoding which only requires the use of types to handle polymorphic cases.
Nevertheless, the use of XSD typed messages is advised to improve interoperability.
XSD types are introduced with typedef definitions in
the header file input to the gSOAP compiler. The type name defined by a typedef definition corresponds to an XML schema
type (XSD type). For example, the following typedef declarations
define various built-in XSD types implemented as primitive C/C++ types:
|
// 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 ... |
Reconsider the getQuote example, now rewritten with explicit XSD types to illustrate the effect:
|
// 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); |
For example, when the client application calls the proxy, the proxy produces a SOAP request with an xsd:string:
|
... <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> ... |
There is no standardized convention for the response element name in a SOAP response message, although it is recommended that the response element name is the method name ending with ``Response''. For example, the response element of getQuote is getQuoteResponse.
The response element name can be specified explicitly using a struct or class declaration in the header file. The struct or class name represents the SOAP response element name used by the service. Consequently, the output parameter of the remote method must be declared as a field of the struct or class. The use of a struct or a class for the service response is fully SOAP 1.1 compliant. In fact, the absence of a struct or class indicates to the gSOAP compiler to automatically generate a struct for the response which is internally used by a stub.
Reconsider the getQuote remote method specification which can be rewritten with an explicit declaration of a SOAP response
element as follows:
|
// Contents of "getQuote.h": typedef char *xsd__string; typedef float xsd__float; struct ns1__getQuoteResponse {xsd__float Result;}; int ns1__getQuote(xsd__string symbol, struct ns1__getQuoteResponse &r); |
|
... <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> ... |
Note that the struct (or class) declaration may appear within the function prototype declaration. For example:
|
// Contents of "getQuote.h": typedef char *xsd__string; typedef float xsd__float; int ns1__getQuote(xsd__string symbol, struct ns1__getQuoteResponse {xsd__float Result;} &r); |
The gSOAP stub and skeleton compiler uses the convention that the last parameter of the function prototype declaration of a remove method in a header file is also the only single output parameter of the method. All other parameters are considered input parameters of the remote method. To specify a remote method with multiple output parameters, a struct or class must be declared for the remote method response, see also 8.1.7. The fields of the struct or class are the output parameters of the remote method. Both the order of the input parameters in the function prototype and the order of the output parameters (the fields in the struct or class) is not significant. However, the SOAP 1.1 specification states that input and output parameters may be treated as having anonymous parameter names which requires a particular ordering, see Section 8.1.13.
As an example, consider a hypothetical remote method getNames with a single input parameter SSN
and two output parameters first and last. This can be specified as:
|
// Contents of file "getNames.h": int ns3__getNames(char *SSN, struct ns3__getNamesResponse {char *first; char *last;} &r); |
|
... <SOAP-ENV:Envelope ... xmlns:ns3="urn:names" ...> ... <ns3:getNames> <SSN>999 99 9999</SSN> </ns3:getNames> ... |
|
... <m:getNamesResponse xmlns:m="urn:names"> <first>John</first> <last>Doe</last> </m:getNamesResponse> ... |
As another example, consider a remote method copy with an input parameter and an output parameter with identical
parameter names (this is not prohibited by the SOAP 1.1 protocol). This can be specified as well using a response struct:
|
// Content of file "copy.h": int X_rox__copy_name(char *name, struct X_rox__copy_nameResponse {char *name;} &r); |
The gSOAP compiler takes the copy.h header file as input and generates the soap_call_X_rox__copy_name proxy. When invoked by a client application, the proxy produces the SOAP request:
|
... <SOAP-ENV:Envelope ... xmlns:X-rox="urn:copy" ...> ... <X-rox:copy-name> <name>SOAP</name> </X-rox:copy-name> ... |
|
... <m:copy-nameResponse xmlns:m="urn:copy"> <name>SOAP</name> </m:copy-nameResponse> ... |
If the single output parameter of a remote method is a complex data type such as a struct or class it is necessary to specify the response element of the remote method as a struct or class at all times. Otherwise, the output parameter will be considered the response element (!), because of the response element specification convention used by gSOAP, as discussed in 8.1.7.
This is is best illustrated with an example. The Flighttracker service by ObjectSpace provides real time flight information for
flights in the air. It requires an airline code and flight number as parameters.
The remote method name is getFlightInfo and
the method has two string parameters: the airline code and flight number, both of which must be encoded as xsd:string types.
The method returns a getFlightResponse response element with a return output parameter that is of complex type
FlightInfo. The type FlightInfo is represented by a class in the header file, whose field names correspond to
the FlightInfo accessors:
|
// Contents of file "flight.h": typedef char *xsd__string; class ns2__FlightInfo { public: xsd__string airline; xsd__string flightNumber; xsd__string altitude; xsd__string currentLocation; xsd__string equipment; xsd__string speed; }; struct ns1__getFlightInfoResponse {ns2__FlightInfo _return;}; int ns1__getFlightInfo(xsd__string param1, xsd__string param2, struct ns1__getFlightInfoResponse &r); |
The gSOAP compiler generates the soap_call_ns1__getFlightInfo proxy. Here is an example fragment of a client application that uses this proxy to request flight information:
|
struct soap soap; ... soap_init(&soap); ... soap_call_ns1__getFlightInfo(&soap, "testvger.objectspace.com/soap/servlet/rpcrouter", "urn:galdemo:flighttracker", "UAL", "184", r); ... 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"}, {"ns1", "urn:galdemo:flighttracker"}, {"ns2", "http://galdemo.flighttracker.com"}, {NULL, NULL} }; |
|
POST /soap/servlet/rpcrouter HTTP/1.1 Host: testvger.objectspace.com Content-Type: text/xml Content-Length: 634 SOAPAction: "urn:galdemo:flighttracker" <?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:galdemo:flighttracker" xmlns:ns2="http://galdemo.flighttracker.com" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <ns1:getFlightInfo xsi:type="ns1:getFlightInfo"> <param1 xsi:type="xsd:string">UAL</param1> <param2 xsi:type="xsd:string">184</param2> </ns1:getFlightInfo> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
|
HTTP/1.1 200 ok Date: Thu, 30 Aug 2001 00:34:17 GMT Server: IBM_HTTP_Server/1.3.12.3 Apache/1.3.12 (Win32) Set-Cookie: sesessionid=2GFVTOGC30D0LGRGU2L4HFA;Path=/ Cache-Control: no-cache="set-cookie,set-cookie2" Expires: Thu, 01 Dec 1994 16:00:00 GMT Content-Length: 861 Content-Type: text/xml; charset=utf-8 Content-Language: en <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <SOAP-ENV:Body> <ns1:getFlightInfoResponse xmlns:ns1="urn:galdemo:flighttracker" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <return xmlns:ns2="http://galdemo.flighttracker.com" xsi:type="ns2:FlightInfo"> <equipment xsi:type="xsd:string">A320</equipment> <airline xsi:type="xsd:string">UAL</airline> <currentLocation xsi:type="xsd:string">188 mi W of Lincoln, NE</currentLocation> <altitude xsi:type="xsd:string">37000</altitude> <speed xsi:type="xsd:string">497</speed> <flightNumber xsi:type="xsd:string">184</flightNumber> </return> </ns1:getFlightInfoResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
|
cout << r.return_.equipment << " flight " << r.return_.airline << r.return_.flightNumber << " traveling " << r.return_.speed << " mph " << " at " << r.return_.altitude << " ft, is located " << r.return_.currentLocation << endl; |
| A320 flight UAL184 traveling 497 mph at 37000 ft, is located 188 mi W of Lincoln, NE |
The SOAP 1.1 protocol allows parameter names to be anonymous. That is, the name(s) of the output
parameters of a remote method are not strictly required to match a client's view of the parameters names. Also, the
input parameter names of a remote method are not strictly required to match a service's view of the parameter names. Although
this convention is likely to be deprecated in SOAP 1.2, the gSOAP compiler can generate stub and skeleton
routines that support anonymous parameters. Parameter names are implicitly
anonymous by omitting the parameter names in the function prototype of the
remote method. For
example:
|
// Contents of "getQuote.h": typedef char *xsd__string; typedef float xsd__float; int ns1__getQuote(xsd__string, xsd__float&); |
For example:
|
// Contents of "getQuote.h": typedef char *xsd__string; typedef float xsd__float; int ns1__getQuote(xsd__string symbol, xsd__float &_return); |
|
// Contents of "getQuote.h": typedef char *xsd__string; typedef float xsd__float; struct ns1__getQuoteResponse {xsd__float _return;}; int ns1__getQuote(xsd__string symbol, struct ns1__getQuoteResponse &r); |
Caution: when anonymous parameter names are used, the order of the parameters in the function prototype of a remote method is significant.
To specify a remote method that has no input parameters, just provide a function prototype with one parameter which is the output
parameter. However, some C/C++ compilers (notably Visual C++TM) will not compile and complain about an empty
struct. This struct is generated by gSOAP to contain the SOAP request message. To fix this, provide one input
parameter of type void* (gSOAP can not serialize void* data). For example:
|
struct ns3__SOAPService { public: int ID; char *name; char *owner; char *description; char *homepageURL; char *endpoint; char *SOAPAction; char *methodNamespaceURI; char *serviceStatus; char *methodName; char *dateCreated; char *downloadURL; char *wsdlURL; char *instructions; char *contactEmail; char *serverImplementation; }; struct ArrayOfSOAPService {struct ns3__SOAPService *__ptr; int __size;}; int ns__getAllSOAPServices(void *_, struct ArrayOfSOAPService &_return); |
Most C/C++ compilers allow empty structs and therefore the void* parameter is not required.
To specify a remote method that has no output parameters, just provide a function prototype with a response struct that is
empty. For example:
|
enum ns__event { off, on, stand_by }; int ns__signal(enum ns__event in, struct ns__signalResponse { } *out); |
Some SOAP resources refer to SOAP RPC with empty responses as one way SOAP messaging. However, we refer to one-way massaging by asynchronous explicit send and receive operations as described in Section 8.3. We found this view of one-way SOAP messaging more useful by providing a message passing alternative to SOAP RPC.
The gSOAP stub and skeleton compiler generates skeleton routines in C++ source form for each of the remote methods specified as function prototypes in the header file processed by the gSOAP compiler. The skeleton routines can be readily used to implement the remote methods in a new SOAP Web service. The compound data types used by the input and output parameters of SOAP remote methods must be declared in the header file, such as structs, classes, arrays, and pointer-based data structures (graphs) that are used as the data types of the parameters of a remote method. The gSOAP compiler automatically generates serializers and deserializers for the data types to enable the generated skeleton routines to encode and decode the contents of the parameters of the remote methods. The gSOAP compiler also generates a remote method request dispatcher routine that will serve requests by calling the appropriate skeleton when the SOAP service application is installed as a CGI application on a Web server.
The following example specifies three remote methods to be implemented by a new SOAP Web service:
|
// Contents of file "calc.h": typedef double xsd__double; int ns__add(xsd__double a, xsd__double b, xsd__double &result); int ns__sub(xsd__double a, xsd__double b, xsd__double &result); int ns__sqrt(xsd__double a, xsd__double &result); |
To generate the skeleton routines, the gSOAP compiler is invoked from the command line with:
| soapcpp2 calc.h |
Here is an example Calculator service application that uses the generated soap_serve routine to handle client requests:
|
// Contents of file "calc.cpp": #include "soapH.h" #include < math.h > // for sqrt() main() { soap_serve(soap_new()); // use the remote method request dispatcher } // Implementation of the "add" remote method: int ns__add(struct soap *soap, double a, double b, double &result) { result = a + b; return SOAP_OK; } // Implementation of the "sub" remote method: int ns__sub(struct soap *soap, double a, double b, double &result) { result = a - b; return SOAP_OK; } // Implementation of the "sqrt" remote method: int ns__sqrt(struct soap *soap, double a, double &result) { if (a > = 0) { result = sqrt(a); return SOAP_OK; } else return soap_receiver_fault(soap, "Square root of negative number", "I can only take the square root of a non-negative number"); } // As always, a namespace mapping table is needed: struct Namespace namespaces[] = { // {"ns-prefix", "ns-name"} {"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"}, {"ns", "urn:simple-calc"}, // bind "ns" namespace prefix {NULL, NULL} }; |
This service application can be readily installed as a CGI application. The service description would be:
|
Unless the CGI application inspects and checks the environment variable SOAPAction which contains the SOAP action request by a client, the SOAP action is ignored by the CGI application. SOAP actions are specific to the SOAP protocol and provide a means for routing requests and for security reasons (e.g. firewall software can inspect SOAP action headers to grant or deny the SOAP request. Note that this requires the SOAP service to check the SOAP action header as well to match it with the remote method.)
The header file input to the gSOAP compiler does not need to be modified to generate client stubs for accessing this service. Client applications can be developed by using the same header file as for which the service application was developed. For example, the soap_call_ns__add stub routine is available from the soapClient.cpp file after invoking the gSOAP compiler on the calc.h header file. As a result, client and service applications can be developed without the need to know the details of the SOAP encoding used.
The deployment of a Web service as a CGI application is an easy means to provide your service on the Internet. gSOAP services can also run as stand-alone services on any port by utilizing the built-in HTTP and TCP/IP stacks. The stand-alone services can be run on port 80 thereby providing Web server capabilities restricted to SOAP RPC.
To create a stand-alone service, only the main routine of the service needs to be modified as follows. Instead of just calling the
soap_serve routine, the main routine is changed into:
|
int main() { struct soap soap; int m, s; // master and slave sockets soap_init(&soap); m = soap_bind(&soap, "machine.cs.fsu.edu", 18083, 100); if (m < 0) soap_print_fault(&soap, stderr); else { fprintf(stderr, "Socket connection successful: master socket = %d\n", m); for (int i = 1; ; i++) { s = soap_accept(&soap); if (s < 0) { soap_print_fault(&soap, stderr); break; } fprintf(stderr, "%d: accepted connection from IP=%d.%d.%d.%d socket=%d", i, (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s); if (soap_serve(&soap) != SOAP_OK) // process RPC request soap_print_fault(&soap, stderr); // print error fprintf(stderr, "request served\n"); soap_destroy(&soap); // clean up class instances soap_end(&soap); // clean up everything and close socket } } soap_done(&soap); // close master socket and detach environment } |
The gSOAP functions that can be used are:
|
The soap.accept_timeout attribute of the gSOAP run-time environment specifies the timeout value for a non-blocking soap_accept(&soap) call. See Section 17.17 for more details on timeout management.
See Section 9.13 for more details on memory management.
A client application connects to this stand-alone service with the endpoint machine.cs.fsu.edu:18083. A client may use the http:// prefix. When absent, no HTTP header is send and no HTTP-based information will be communicated to the service.
Multi-threading a Web Service is essential when the response times for handling requests by the service are (potentially) long or when keep-alive is enabled, see Section 17.11. In case of long response times, the latencies introduced by the unrelated requests may become prohibitive for a successful deployment of a stand-alone service. When HTTP keep-alive is enabled, a client may not close the socket on time, thereby preventing other clients from connecting.
gSOAP 2.0 and higher is thread safe and supports the implementation of multi-threaded stand-alone services in which a thread is used to handle a request.
The following example illustrates the use of threads to improve the quality of service by handling new requests in separate threads:
|
#include "soapH.h" #include < pthread.h > #define BACKLOG (100) // Max. request backlog int main(int argc, char **argv) { struct soap soap; soap_init(&soap); if (argc < 3) // no args: assume this is a CGI application { soap_serve(&soap); // serve request, one thread, CGI style soap_destroy(&soap); // dealloc C++ data soap_end(&soap); // dealloc data and clean up } else { soap.send_timeout = 60; // 60 seconds soap.recv_timeout = 60; // 60 seconds soap.accept_timeout = 3600; // server stops after 1 hour of inactivity soap.max_keep_alive = 100; // max keep-alive sequence void *process_request(void*); struct soap *tsoap; pthread_t tid; int port = atoi(argv[1]); // first command-line arg is port int m, s; m = soap_bind(&soap, NULL, port, BACKLOG); if (m < 0) exit(1); fprintf(stderr, "Socket connection successful %d\n", m); for (;;) { s = soap_accept(&soap); if (s < 0) { if (soap.errnum) { soap_print_fault(&soap, stderr); exit(1); } fprintf(stderr, "server timed out\n"); break; } fprintf(stderr, "Thread %d accepts socket %d connection from IP %d.%d.%d.%d\n", i, s, (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF); tsoap = soap_copy(&soap); // make a safe copy if (!tsoap) break; pthread_create(&tid, NULL, (void*(*)(void*))process_request, (void*)tsoap); } } soap_done(&soap); // detach soap struct return 0; } void *process_request(void *soap) { pthread_detach(pthread_self()); soap_serve((struct soap*)soap); soap_destroy((struct soap*)soap); // dealloc C++ data soap_end((struct soap*)soap); // dealloc data and clean up soap_done((struct soap*)soap); // detach soap struct free(soap); return NULL; } |
The soap_serve dispatcher handles one request or multiple requests when HTTP keep-alive is set with SOAP_IO_KEEPALIVE. The soap.max_keep_alive value can be set to the maximum keep-alive calls allowed, which is important to avoid a client from holding a thread indefinitely. The send and receive timeouts are set to avoid (intentionally) slow clients from holding a socket connection too long. The accept timeout is used to let the server terminate automatically after a period of inactivity.
The following example uses a pool of threads:
|
#include "soapH.h" #include < pthread.h > #define BACKLOG (100) // Max. request backlog #define MAX_THR (8) // Max. threads to serve requests int main(int argc, char **argv) { struct soap soap; soap_init(&soap); if (argc < 3) // no args: assume this is a CGI application { soap_serve(&soap); // serve request, one thread, CGI style soap_destroy(&soap); // dealloc C++ data soap_end(&soap); // dealloc data and clean up } else { struct soap *soap_thr[MAX_THR]; // each thread needs a runtime environment pthread_t tid[MAX_THR]; int port = atoi(argv[1]); // first command-line arg is port int m, s, i; m = soap_bind(&soap, NULL, port, BACKLOG); if (m < 0) exit(1); fprintf(stderr, "Socket connection successful %d\n", m); for (i = 0; i < MAX_THR; i++) soap_thr[i] = NULL; for (;;) { for (i = 0; i < MAX_THR; i++) { s = soap_accept(&soap); if (s < 0) break; fprintf(stderr, "Thread %d accepts socket %d connection from IP %d.%d.%d.%d\n", i, s, (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF); if (!soap_thr[i]) // first time around { soap_thr[i] = soap_copy(&soap); if (!soap_thr[i]) exit(1); // could not allocate } else\ // recycle soap environment { pthread_join(tid[i], NULL); fprintf(stderr, "Thread %d completed\n", i); soap_destroy(soap_thr[i]); // deallocate C++ data of old thread soap_end(soap_thr[i]); // deallocate data of old thread } soap_thr[i]->socket = s; // new socket fd pthread_create(&tid[i], NULL, (void*(*)(void*))soap_serve, (void*)soap_thr[i]); } } } return 0; } |
|
For clean termination of the server, the master socket can be closed and callbacks removed with soap_done(struct soap *soap).
The void *soap.user field can be used to pass application data to service methods. This field should be set before the soap_serve() call. The service method can access this field to use the application-dependent data. The following example shows how a non-static database handle is initialized and passed to the service methods:
|
{ ... struct soap soap; database_handle_type database_handle; soap_init(&soap); soap.user = (void*)database_handle; ... soap_serve(&soap); // call the remove method dispatcher to handle request ... } int ns__myMethod(struct soap *soap, ...) { ... fetch((database_handle_type*)soap->user); // get data ... return SOAP_OK; } |
The same client header file specification issues apply to the specification and implementation of a SOAP Web service. Refer to
Server object classes for C++ server applications are automatically generated by the gSOAP compiler.
We illustrate the generation of an object class with a calculator example.
|
// Content of file "calc.h": //gsoap ns service name: Calculator //gsoap ns service style: rpc //gsoap ns service encoding: encoded //gsoap ns service location: http://www.cs.fsu.edu/~engelen/calc.cgi //gsoap ns schema namespace: urn:calc //gsoap ns service method-action: add "" int ns__add(double a, double b, double &result); int ns__sub(double a, double b, double &result); int ns__mul(double a, double b, double &result); int ns__div(double a, double b, double &result); |
|
#include "soapH.h" class Calculator : public soap { public: Quote() { soap_init(this); }; ~Quote() { soap_destroy(this); soap_end(this); soap_done(this); }}; int serve() { return soap_serve(this); }; }; |
|
#include "soapCalculatorObject.h" // get server object #include "Calculator.nsmap" // get namespace bindings int main() { Calculator c; return c.serve(); // calls soap_serve to serve as CGI application (using stdin/out) } int ns__add(double a, double b, double &result) { result = a + b; return SOAP_OK; } ... sub(), mul(), and div() implementations ... |
The gSOAP stub and skeleton compiler soapcpp2 generates WSDL (Web Service Description Language) service descriptions and XML schema files when processing a header file. The compiler produces one WSDL file for a set of remote methods. The names of the function prototypes of the remote methods must use the same namespace prefix and the namespace prefix is used to name the WSDL file. If multiple namespace prefixes are used to define remote methods, multiple WSDL files will be created and each file describes the set of remote methods belonging to a namespace prefix.
In addition to the generation of the ns.wsdl file, a file with a namespace mapping table is generated by the gSOAP
compiler. An example mapping table is shown below:
|
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", \"http://www.w3.org/*/XMLSchema-instance"}, {"xsd", "http://www.w3.org/2001/XMLSchema", \"http://www.w3.org/*/XMLSchema"}, {"ns", "http://tempuri.org"}, {NULL, NULL} }; |
To deploy a Web service, copy the compiled CGI service application to the designated CGI directory of your Web server. Make sure the proper file permissions are set (chmod 755 calc.cgi for Unix/Linux). You can then publish the WSDL file on the Web by placing it in the appropriate Web server directory.
The gSOAP compiler also generates XML schema files for all C/C++ complex types (e.g. structs and classes) when declared with a namespace prefix. These files are named ns.xsd, where ns is the namespace prefix used in the declaration of the complex type. The XML schema files do not have to be published as the WSDL file already contains the appropriate XML schema definitions.
For example, suppose the following methods are defined in the header file:
|
typedef double xsd__double; int ns__add(xsd__double a, xsd__double b, xsd__double &result); int ns__sub(xsd__double a, xsd__double b, xsd__double &result); int ns__sqrt(xsd__double a, xsd__double &result); |
|
<?xml version="1.0" encoding="UTF-8"?> <definitions name="Service" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://location/Service.wsdl" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:WSDL="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2000/10/XMLSchema" xmlns:tns="http://location/Service.wsdl" xmlns:ns="http://tempuri.org"> <types> <schema xmlns="http://www.w3.org/2000/10/XMLSchema" targetNamespace="http://tempuri.org" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"> <complexType name="addResponse"> <all> <element name="result" type="double" minOccurs="0" maxOccurs="1"/> </all> <anyAttribute namespace="##other"/> </complexType> <complexType name="subResponse"> <all> <element name="result" type="double" minOccurs="0" maxOccurs="1"/> </all> <anyAttribute namespace="##other"/> </complexType> <complexType name="sqrtResponse"> <all> <element name="result" type="double" minOccurs="0" maxOccurs="1"/> </all> <anyAttribute namespace="##other"/> </complexType> </schema> </types> <message name="addRequest"> <part name="a" type="xsd:double"/> <part name="b" type="xsd:double"/> </message> <message name="addResponse"> <part name="result" type="xsd:double"/> </message> <message name="subRequest"> <part name="a" type="xsd:double"/> <part name="b" type="xsd:double"/> </message> <message name="subResponse"> <part name="result" type="xsd:double"/> </message> <message name="sqrtRequest"> <part name="a" type="xsd:double"/> </message> <message name="sqrtResponse"> <part name="result" type="xsd:double"/> </message> <portType name="ServicePortType"> <operation name="add"> <input message="tns:addRequest"/> <output message="tns:addResponse"/> </operation> <operation name="sub"> <input message="tns:subRequest"/> <output message="tns:subResponse"/> </operation> <operation name="sqrt"> <input message="tns:sqrtRequest"/> <output message="tns:sqrtResponse"/> </operation> </portType> <binding name="ServiceBinding" type="tns:ServicePortType"> <SOAP:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="add"> <SOAP:operation soapAction="http://tempuri.org#add"/> <input> <SOAP:body use="encoded" namespace="http://tempuri.org" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <SOAP:body use="encoded" namespace="http://tempuri.org" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> <operation name="sub"> <SOAP:operation soapAction="http://tempuri.org#sub"/> <input> <SOAP:body use="encoded" namespace="http://tempuri.org" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <SOAP:body use="encoded" namespace="http://tempuri.org" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> <operation name="sqrt"> <SOAP:operation soapAction="http://tempuri.org#sqrt"/> <input> <SOAP:body use="encoded" namespace="http://tempuri.org" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <SOAP:body use="encoded" namespace="http://tempuri.org" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding> <service name="Service"> <port name="ServicePort" binding="tns:ServiceBinding"> <SOAP:address location="http://location/Service.cgi"/> </port> </service> </definitions> |
Note: see README.txt in the wsdl directory for installation instructions for the WSDL parser and importer.
The creation of SOAP Web Services applications from a WSDL service description is a two-step process.
First, execute wsdl2h file.wsdl which generates the a C++ header file file.h (use wsdl2h -c file.wsdl to generate pure C code). You can provide a URL instead of a file name, when applicable. The generated header file is a Web service specification that contains the parameter types and service function definitions. The functions are represented as function prototypes. The file contains various annotations related to the Web service. The header file must be processed by the gSOAP compiler. You cannot use it with a C/C++ compiler directly.
Second, the header file file.h is processed by the gSOAP compiler by executing soapcpp2 -i file.h. This creates the C/C++ source files to build a client application, see 8.1. In addition, this generates a client proxy object declared in soapServiceProxy.h, where Service is the name of the service defined in the WSDL. To use this object, include the soapServiceProxy.h and Service.nsmap files in your C++ client application. The Service class provides the remote Web service methods as class members.
Consider the following example commands (entered at the command prompt):
|
$ wsdl2h -o Amazon.h http://soap.amazon.com/schemas/AmazonWebServices.wsdl ... $ soapcpp2 -i Amazon.h |
When parsing a WSDL, the output file name is the WSDL input file name with extension .h instead of .wsdl. When an input file is absent or a WSDL file from a Web location is accessed, the header output will be produced on the standard output. Schema files (.xsd) can also be parsed and processed.
The wsdl2h command line options are:
|
|
# This file contains custom definitions of the XML schema types and # C/C++ types for your project, and XML namespace prefix definitions. # XML namespace prefix definitions can be provided to override the # default choice of ns1, ns2, ... prefixes. For example: i = "http://www.soapinterop.org/" s = "http://www.soapinterop.org/xsd" # Type definitions consists of a single line containing: # type = declaration | use | pointer-use # where # type is the XML schema type (or an application type in a namespace # that has a prefix definition given as above). # declaration is an optional C/C++ type declaration # use is how the type is referred to in code # pointer-use is how the type should be referred to as a pointer (opt) # Example XML schema and C/C++ type bindings: xsd__int = | int xsd__byte = | char | int* xsd__boolean = enum xsd__boolean false_, true_ ; | enum xsd__boolean xsd__base64Binary = class xsd__base64Binary unsigned char *__ptr; int __size; ; | xsd__base64Binary | xsd__base64Binary |
A gSOAP service may make client calls to other services from within its remove methods. This is best illustrated with an example. The following example is a more sophisticated example that combines the functionality of two Web services into one new SOAP Web service. The service provides a currency-converted stock quote. To serve a request, the service in turn requests the stock quote and the currency-exchange rate from two XMethods services.
In addition to being a client of two XMethods services, this service application can also be used as a client of itself to test the implementation. As a client invoked from the command-line, it will return a currency-converted stock quote by connecting to a copy of itself installed as a CGI application on the Web to retrieve the quote after which it will print the quote on the terminal.
The header file input to the gSOAP compiler is given below:
|
// Contents of file "quotex.h": int ns1__getQuote(char *symbol, float &result); // XMethods delayed stock quote service remote method int ns2__getRate(char *country1, char *country2, float &result); // XMethods currency-exchange service remote method int ns3__getQuote(char *symbol, char *country, float &result); // the new currency-converted stock quote service |
|
// Contents of file "quotex.cpp": #include "soapH.h" // include generated proxy and SOAP support int main(int argc, char **argv) { struct soap soap; float q; soap_init(&soap); if (argc < = 2) soap_serve(&soap); else if (soap_call_ns3__getQuote(&soap, "http://www.cs.fsu.edu/~engelen/quotex.cgi", "", argv[1], argv[2], q)) soap_print_fault(&soap, stderr); else printf("\nCompany %s: %f (%s)\n", argv[1], q, argv[2]); return 0; } int ns3__getQuote(struct soap *soap, char *symbol, char *country, float &result) { float q, r; int socket = soap->socket; // save socket (stand-alone service only, does not support keep-alive) if (soap_call_ns1__getQuote(soap, "http://services.xmethods.net/soap", "", symbol, &q) == 0 && soap_call_ns2__getRate(soap, "http://services.xmethods.net/soap", NULL, "us", country, &r) == 0) { result = q*r; soap->socket = socket; return SOAP_OK; } soap->socket = socket; return SOAP_FAULT; // pass soap fault messages on to the client of this app } /* Since this app is a combined client-server, it is put together with * one header file that describes all remote methods. However, as a consequence we * have to implement the methods that are not ours. Since these implementations are * never called (this code is client-side), we can make them dummies as below. */ int ns1__getQuote(struct soap *soap, char *symbol, float &result) { return SOAP_NO_METHOD; } // dummy: will never be called int ns2__getRate(struct soap *soap, char *country1, char *country2, float &result) { return SOAP_NO_METHOD; } // dummy: will never be called 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", "http://www.w3.org/*/XMLSchema-instance"}, {"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema"}, {"ns1", "urn:xmethods-delayed-quotes"}, {"ns2", "urn:xmethods-CurrencyExchange"}, {"ns3", "urn:quotex"}, {NULL, NULL} }; |
|
soapcpp2 quotex.h g++ -o quotex.cgi quotex.cpp soapC.cpp soapClient.cpp soapServer.cpp stdsoap2.cpp -lsocket -lxnet -lnsl |
The quotex.cgi executable is installed as a CGI application on the Web by
copying it in the designated directory specific to your Web server. After
this, the executable can also serve to test the service. For example
| quotex.cgi IBM uk |
When combining clients and service functionalities, it is required to use one header file input to the compiler. As a consequence, however, stubs and skeletons are available for all remote methods, while the client part will only use the stubs and the service part will use the skeletons. Thus, dummy implementations of the unused remote methods need to be given which are never called.
Three WSDL files are created by gSOAP: ns1.wsdl, ns2.wsdl, and ns3.wsdl. Only the ns3.wsdl file is required to be published as it contains the description of the combined service, while the others are generated as a side-effect (and in case you want to develop these separate services).
SOAP RPC client-server interaction is synchronous: the client blocks until the server responds to the request. gSOAP also supports asynchronous one-way message passing. SOAP messaging routines are declared as function prototypes, just like remote methods for SOAP RPC. However, the output parameter is a void type to indicate the absence of a return value.
For example, the following header file specifies a event message for SOAP messaging:
| int ns__event(int eventNo, void dummy); |
|
int soap_send_ns__event(struct soap *soap, const char URL, const char action, int event); int soap_recv_ns__event(struct soap *soap, struct ns__event *dummy); |
The soap_recv_ns__event function waits for a SOAP message on the currently open socket (soap.socket) and fills the
struct ns__event with the ns__event parameters (e.g. int eventNo).
The struct ns__event is automatically created by gSOAP and is a mirror image of the ns__event parameters:
|
struct ns__event { int eventNo; } |
| int soap_serve_ns__event(struct soap *soap); |
As usual, the skeleton will be automatically called by the remote method request dispatcher that handles both the remote method
requests (RPCs) and messages:
|
main() { soap_serve(soap_new()); } int ns__event(struct soap *soap, int eventNo) { ... // handle event return SOAP_OK; } |
The gSOAP stub and skeleton compiler generates serializers and deserializers for all user-defined data structures that are specified in the header file input to the compiler. The serializers and deserializers can be found in the generated soapC.cpp file. These serializers and deserializers can be used separately by an application without the need to build a full client or service application. This is useful for applications that need to save or export their data in XML or need to import or load data stored in XML format.
The following attributes can be set to control the destination and source for serialization and deserialization:
|
|
See also Section 9.12 to control the I/O buffering and content encoding such as compression and DIME encoding.
To serialize a data type to a stream, two functions should be called to prepare for serialization of the data and to send the data, respectively. The first function, soap_serialize, analyzes pointers and determines if multi-references are required to encode the data and if cycles are present the object graph. The second function, soap_put, produces the XML output on a stream.
The soap_serialize and soap_put function names are specific to a data type. For example, soap_serialize_float(&soap, &d) is called to serialize an float value and soap_put_float(&soap, &d, "number", NULL) is called to output the floating point value in SOAP tagged with the name <number>. To initialize data, the soap_default function of a data type can be used. For example, soap_default_float(&soap, &d) initializes the float to 0.0. The soap_default functions are useful to initialize complex data types such as arrays, structs, and class instances. Note that the soap_default functions do not need the gSOAP runtime environment as a first parameter.
The following table lists the type naming conventions used by gSOAP:
|
| struct ns__Person { char *name; } *p; |
| soap_serialize_PointerTons__Person(&soap, &p); |
|
soap_begin_send(&soap); soap_put_PointerTons__Person(&soap, &p, "ns:element-name", "ns:type-name"); soap_end_send(&soap); |
|
<ns:element-name xmlns:SOAP-ENV="..." xmlns:SOAP-ENC="..." xmlns:ns="..." ... xsi:type="ns:type-name"> <name xsi:type="xsd:string">...</name> </ns:element-name> |
You can assign an
output stream to soap.os or a file descriptor to soap.sendfd.
For example
|
soap.sendfd = open(file, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR); soap_serialize_PointerTons__Person(&soap, &p); soap_begin_send(&soap); soap_put_PointerTons__Person(&soap, &p, "ns:element-name", "ns:type-name"); soap_end_send(&soap); |
To save the data as an XML tree (with one root) without any id-ref attributes, use the SOAP_XML_TREE flag. The data structure MUST NOT contain pointer-based cycles.
To preserve the exact structure of the data object graph and create XML with one root, use the SOAP_XML_GRAPH output-mode flag (see Section 9.12). Use this flag and the soap_serialize function to prepare the serialization of data with in-line id-ref attributes. Using the SOAP_XML_GRAPH flag assures the preservation of the logical structure of the data
For example, to encode the contents of two variables var1 and var2 that may
share data throug pointer structures,
the serializers are called before the output routines:
|
T1 var1; T2 var2; struct soap soap; ... soap_init(&soap); // initialize [soap_omode(&soap, flags);] // set output-mode flags (e.g. SOAP_ENC_XML|SOAP_ENC_ZLIB) soap_begin(&soap); // start new (de)serialization phase soap_set_omode(&soap, SOAP_XML_GRAPH); soap_serialize_Type1(&soap, &var1); soap_serialize_Type2(&soap, &var2); ... [soap.socket = a_socket_file_descriptor;] // when using sockets [soap.os = an_output_stream;] // C++ [soap.sendfd = an_output_file_descriptor;] // C soap_begin_send(&soap); soap_put_Type1(&soap, &var1, "[namespace-prefix:]element-name1", "[namespace-prefix:]type-name1"); soap_put_Type2(&soap, &var2, "[namespace-prefix:]element-name2", "[namespace-prefix:]type-name2"); ... soap_end_send(&soap); // flush soap_end(&soap); // remove temporary data structures after phase soap_done(&soap); // finalize last use of this environment ... |
For serializing class instances, method invocations MUST be used instead of function calls, for example obj.soap_serialize(&soap) and obj.soap_put(&soap, "elt", "type"). This ensures that the proper serializers are used for serializing instances of derived classes.
You can serialize a class instance to a stream as follows:
|
struct soap soap; myClass obj; soap_init(&soap); // initialize soap_begin(&soap); // start new (de)serialization phase soap_set_omode(&soap, SOAP_XML_GRAPH); obj.serialize(&soap); soap.os = cout; // send to cout soap_begin_send(&soap); obj.put(&soap, "[namespace-prefix:]element-name1", "[namespace-prefix:]type-name1"); ... soap_end_send(&soap); // flush soap_end(&soap); // remove temporary data structures after phase soap_done(&soap); // finalize last use of this environment |
|
ostream &operator<<(ostream &o, const myClass &e) { if (!e.soap) ... error: need a soap struct to serialize (could use global struct) ... else { ostream *os = e.soap->os; e.soap->os = &o; soap_set_omode(e.soap, SOAP_XML_GRAPH); e.serialize(e.soap); soap_begin_send(e.soap); e.put(e.soap, "myClass", NULL); soap_end_send(e.soap); e.soap->os = os; soap_clr_omode(e.soap, SOAP_XML_GRAPH); } return o; } |
In principle, XML output for a data structure can be produced with soap_put without calling the soap_serialize function first. In this case, the result is similar to SOAP_XML_TREE which means that no id-refs are output. Cycles in the data structure will crash the serialization algorithm, even when the SOAP_XML_GRAPH is set.
Consider the following struct:
|
// Contents of file "tricky.h": struct Tricky { int *p; int n; int *q; }; |
|
struct soap soap; struct Tricky X; X.n = 1; X.p = &X.n; X.q = &X.n; soap_init(&soap); soap_begin(&soap); soap_serialize_Tricky(&soap, &X); soap_put_Tricky(&soap, &X, "Tricky", NULL); soap_end(&soap); // Clean up temporary data used by the serializer |
The resulting output is:
|
<Tricky xsi:type="Tricky"> <p href="#2"/> <n xsi:type="int">1</n> <q href="#2"/> <r xsi:type="int">2</r> </Tricky> <id id="2" xsi:type="int">1</id> |
With the SOAP_XML_GRAPH flag the output is:
|
<Tricky xsi:type="Tricky"> <p href="#2"/> <n id="2" xsi:type="int">1</n> <q href="#2"/> </Tricky> |
To deserialize a data type, its soap_get function is used. The outline of a program that deserializes two variables var1 and var2 is for example:
|
T1 var1; T2 var2; struct soap soap; ... soap_init(&soap); // initialize at least once [soap_imode(&soap, flags);] // set input-mode flags soap_begin(&soap); // begin new decoding phase [soap.is = an_input_stream;] // C++ [soap.recvfd = an_input_file_desriptpr;] // C soap_begin_recv(&soap); // if HTTP/MIME/DIME/GZIP headers are present, parse them if (!soap_get_Type1(&soap, &var1, "[namespace-prefix:]element-name1", "[namespace-prefix:]type-name1")) ... error ... if (!soap_get_Type2(&soap, &var2, "[namespace-prefix:]element-name2", "[namespace-prefix:]type-name1")) ... error ... ... soap_end_recv(&soap); // check consistency of id/hrefs soap_destroy(&soap); // remove deserialized class instances soap_end(&soap); // remove temporary data, including the decoded data on the heap soap_done(&soap); // finalize last use of the environment |
The soap_begin call resets the deserializers. The soap_destroy and soap_end calls remove the temporary data structures and the decoded data that was placed on the heap.
To remove temporary data while retaining the deserialized data on the heap, the function soap_free should be called instead of soap_destroy and soap_end.
One call to the soap_get_Type function of a type Type scans the entire input to process its XML content and to capture SOAP 1.1 independent elements (which contain multi-referenced objects). As a result, soap.error will set to SOAP_EOF. Also storing multiple objects into one file will fail to decode them properly with multiple soap_get calls. A well-formed XML document should only have one root anyway, so don't save multiple objects into one file. If you must save multiple objects, create a linked list or an array of objects and save the linked list or array. You could use the soap_in_Type function instead of the soap_get_Type function. The soap_in_Type function parses one XML element at a time.
You can deserialize class instances from a stream as follows:
|
myClass obj; struct soap soap; soap_init(&soap); // initialize soap_begin(&soap); // begin new decoding phase soap.is = cin; // read from cin soap_begin_recv(&soap); // if HTTP header is present, parse it if (!obj.get(&soap, "myClass", NULL) ... error ... soap_end_recv(&soap); // check consistency of id/hrefs ... soap_destroy(&soap); // remove deserialized class instances soap_end(&soap); // remove temporary data, including the decoded data on the heap soap_done(&soap); // finalize last use of the environment |
|
istream &operator>>(istream &i, myClass &e) { if (!e.soap) ... error: need soap struct to deserialize (could use global struct)... istream *is = e.soap->is; e.soap->is = &i; if (soap_begin_recv(e.soap) || e.in(e.soap, NULL, NULL) || soap_end_recv(e.soap)) ... error ... e.soap->is = is; return i; } |
As an example, consider the following data type declarations:
|
// Contents of file "person.h": typedef char *xsd__string; typedef char *xsd__Name; typedef unsigned int xsd__unsignedInt; enum ns__Gender {male, female}; class ns__Address { public: xsd__string street; xsd__unsignedInt number; xsd__string city; }; class ns__Person { public: xsd__Name name; enum ns__Gender gender; ns__Address address; ns__Person *mother; ns__Person *father; }; |
|
// Contents of file "person.cpp": #include "soapH.h" int main() { struct soap soap; ns__Person mother, father, john; mother.name = "Mary"; mother.gender = female; mother.address.street = "Downing st."; mother.address.number = 10; mother.address.city = "London"; mother.mother = NULL; mother.father = NULL; father.name = "Stuart"; father.gender = male; father.address.street = "Main st."; father.address.number = 5; father.address.city = "London"; father.mother = NULL; father.father = NULL; john.name = "John"; john.gender = male; john.address = mother.address; john.mother = &mother; john.father = &father; soap_init(&soap); soap_omode(&soap, SOAP_ENC_ZLIB | SOAP_XML_GRAPH); // see 9.12 soap_begin(&soap); soap_begin_send(&soap); john.soap_serialize(&soap); john.soap_put(&soap, "johnnie", NULL); soap_end_send(&soap); soap_end(&soap); soap_done(&soap); } 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"}, {"ns", "urn:person"}, // Namespace URI of the ``Person'' data type {NULL, NULL} }; |
|
soapcpp2 person.h g++ -DWITH_GZIP -o person person.cpp soapC.cpp stdsoap2.cpp -lsocket -lxnet -lnsl -lz |
Running the person application results in the compressed XML output:
|
<johnnie xsi:type="ns:Person" 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:ns="urn:person" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <name xsi:type="xsd:Name">John</name> <gender xsi:type="ns:Gender">male</gender> <address xsi:type="ns:Address"> <street id="3" xsi:type="xsd:string">Dowling st.</street> <number xsi:type="unsignedInt">10</number> <city id="4" xsi:type="xsd:string">London</city> </address> <mother xsi:type="ns:Person"> <name xsi:type="xsd:Name">Mary</name> <gender xsi:type="ns:Gender">female</gender> <address xsi:type="ns:Address"> <street href="#3"/> <number xsi:type="unsignedInt">5</number> <city href="#4"/> </address> </mother> <father xsi:type="ns:Person"> <name xsi:type="xsd:Name">Stuart</name> <gender xsi:type="ns:Gender">male</gender> <address xsi:type="ns:Address"> <street xsi:type="xsd:string">Main st.</street> <number xsi:type="unsignedInt">13</number> <city href="#4"/> </address> </father> </johnnie> |
|
#include "soapH.h" int main() { struct soap soap; ns__Person *mother, *father, *john = NULL; soap_init(&soap); soap_imode(&soap, SOAP_ENC_ZLIB); // optional: gzip is detected automatically soap_begin(&soap); soap_begin_recv(&soap); if (soap_get_ns__Person(&soap, john, "johnnie", NULL)) ... error ... mother = john->mother; father = john->father; ... soap_end_recv(&soap); soap_free(&soap); // Clean up temporary data but keep deserialized data } 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"}, {"ns", "urn:person"}, // Namespace URI of the ``Person'' data type {NULL, NULL} }; |
|
john = soap_get_ns__Person(&soap, NULL, "johnnie", NULL); |
Alternatively, the XML content can be decoded within an existing allocated data structure.
The following program fragment decodes the SOAP content in a struct ns__Person allocated on the stack:
|
#include "soapH.h" main() { struct soap soap; ns__Person *mother, *father, john; soap_init(&soap); soap_imode(&soap, SOAP_ENC_ZLIB); // optional soap_begin(&soap); soap_begin_recv(&soap); soap_default_ns__Person(&soap, &john); if (soap_get_ns__Person(&soap, &john, "johnnie", NULL)) ... error ... ... } struct Namespace namespaces[] = ... |
C++ applications can define appropriate stream operations on objects for (de)serialization of objects on streams.
This is best illustrated with an example.
Consider the class
|
class ns__person { public: char *name; struct soap *soap; // we need this, see below ns__person(); ~ns__person(); }; |
| soapcpp2 person.h |
|
#include "soapH.h" #include "ns.nsmap" ... struct soap *soap = soap_new(); ns__person *p = soap_new_ns__person(soap, -1); ... cout << p; // serialize p in XML ... in >> p; // parse XML and deserialize p ... soap_destroy(soap); // deletes p too soap_end(soap); soap_done(soap); |
|
ostream &operator<<(ostream &o, const ns__person &p) { if (!p->soap) return o; // need a gSOAP environment to serialize p.soap->os = &o; soap_omode(p.soap, SOAP_XML_GRAPH); // XML tree or graph p->soap_serialize(p.soap); soap_begin_send(p.soap); if (p->soap_put(p.soap, "person", NULL) | | soap_end_send(p.soap)) ; // handle I/O error return o; } istream &operator>>(istream &i, ns__person &p) { if (!p->soap) return o; // need a gSOAP environment to parse XML and deserialize p.soap->is = &i; if (soap_begin_recv(p.soap) | | p->soap_in(p.soap, NULL, NULL) | | soap_end_recv(p.soap)) ; // handle I/O error return i; } |
The gSOAP compiler generates soap_default functions for all data types. The default values of the primitive types can be
easily changed by defining any of the following macros in the stdsoap2.h file:
|
#define SOAP_DEFAULT_bool #define SOAP_DEFAULT_byte #define SOAP_DEFAULT_double #define SOAP_DEFAULT_float #define SOAP_DEFAULT_int #define SOAP_DEFAULT_long #define SOAP_DEFAULT_LONG64 #define SOAP_DEFAULT_short #define SOAP_DEFAULT_string #define SOAP_DEFAULT_time #define SOAP_DEFAULT_unsignedByte #define SOAP_DEFAULT_unsignedInt #define SOAP_DEFAULT_unsignedLong #define SOAP_DEFAULT_unsignedLONG64 #define SOAP_DEFAULT_unsignedShort #define SOAP_DEFAULT_wstring |
Default values can also be assigned to individual struct and class fields of primitive type. For example,
|
struct MyRecord { char *name = "Unknown"; int value = 9999; enum Status { active, passive } status = passive; } |
Because method requests and responses are essentially structs, default values can also be assigned to method parameters. The
default parameter values do not control the parameterization of C/C++ function calls, i.e. all actual parameters must be present
when calling a function. The default parameter values are used in case an inbound request or response message lacks the XML
elements with parameter values. For example, a Web service can use default values to fill-in absent parameters in a
SOAP/XML request:
| int ns__login(char *uid = "anonymous", char *pwd = "guest", bool granted); |
|
<?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:ns="http://tempuri.org"> <SOAP-ENV:Body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <ns:login> </ns:login> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
The gSOAP stub and skeleton compiler is invoked from the command line and optionally takes the name of a header file as an
argument or, when the file name is absent, parses the standard input:
| soapcpp2 [aheaderfile.h] |
|
The soapClientLib.cpp and soapServerLib.cpp can be used to build (dynamic) client and server libraries. The serialization routines are local (static) to avoid link symbol conflicts. You must create a separate library for SOAP Header and Fault handling, as described in Section 17.32.
The following files are part of the gSOAP package and are required to build client and service applications:
|
The compiler supports the following options:
|
| soapcpp2 -cd '../projects' -pmy file.h |
|
../projects/myH.h ../projects/myC.c ../projects/myClient.c ../projects/myServer.c ../projects/myStub.h |
| soapcpp2 /cd '..\projects' /pmy file.h |
|
// Generate pure C and do not produce WSDL output: //gsoapopt cw int ns__myMethod(char*,char**); // takes a string and returns a string |
gSOAP supports SOAP 1.1 by default. SOAP 1.2 support is automatically turned on when the appropriate SOAP 1.2 namespace is used
in the namespace mapping table:
|
struct Namespace namespaces[] = { {"SOAP-ENV", "http://www.w3.org/2002/06/soap-envelope"}, {"SOAP-ENC", "http://www.w3.org/2002/06/soap-encoding"}, {"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance"}, {"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema"}, ... } |
|
struct Namespace namespaces[] = { {"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/2002/06/soap-encoding"}, {"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/2002/06/soap-envelope"}, {"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance"}, {"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema"}, ... } |
The gSOAP soapcpp2 compiler generates a .nsmap file with SOAP-ENV and SOAP-ENC namespace patterns similar to the above. Since clients issue a send first, they will always use SOAP 1.1 for requests when the namespace table is similar as shown above. Clients can accept SOAP 1.2 responses by inspecting the response message. To restrict gSOAP services and clients to SOAP 1.2 and to generate SOAP 1.2 service WSDLs, use soapcpp2 compiler option -2 to generate SOAP 1.2 conformant .nsmap and .wsdl files.
Caution: SOAP 1.2 does not support partially transmitted arrays. So the __offset field of a dynamic array is meaningless.
Caution: SOAP 1.2 requires the use of SOAP_ENV__Code, SOAP_ENV__Reason, and SOAP_ENV__Detail fields in a SOAP_ENV__Fault fault struct, while SOAP 1.1 uses faultcode, faultstring, and detail fields. Use soap_receiver_fault(struct soap *soap, const char *faultstring, const char *detail) to set a SOAP 1.1/1.2 fault at the server-side. Use soap_sender_fault(struct soap *soap, const char *faultstring, const char *detail) to set a SOAP 1.1/1.2 unrecoverable Bad Request fault at the server-side.
The soapdefs.h header file is included in stdsoap2.h when compiling with option -DWITH_SOAPDEFS_H:
| g++ -DWITH_SOAPDEFS_H -c stdsoap2.cpp |
|
// Contents of soapdefs.h #include < ostream > #define SOAP_BUFLEN 20480 // use large send/recv buffer |
|
extern class ostream; // ostream can't be (de)serialized, but need to be declared to make it visible to gSOAP class ns__myClass { ... virtual void print(ostream &s) const; // need ostream here ... }; |
The #module directive is used to build modules. A library can be build from a module and linked with multiple Web services applications. The directive should appear at the top of the header file and has the following format:
|
#module "name" |
For example:
|
/* Contents of module.h */ #module "test" long; char*; struct ns__S { ... } |
A module MUST be imported into another header file to use it and you cannot use a module alone to build a SOAP or XML application. That is, the top most header file in the import tree SHOULD NOT be a module.
When multiple modules are linked, the types that they declare MUST be declared in one module only to avoid name clashes and link errors. You cannot create two modules that share the same type declaration and link the modules. When necessary, you should consider creating a module hierarchy such that types are declared only once and by only one module when these modules must be linked.
The #import directive is used to include gSOAP header files into other gSOAP header files for processing with the gSOAP compiler soapcpp2. The C #include directive cannot be used to include gSOAP header files. The #include directive is reserved to control the post-gSOAP compilation process, see 9.6.
The #import directive is used for two purposes: you can use it to include the contents of a header file into another header file and you can use it to import a module, see 9.4.
An example of the #import directive:
|
#import "mydefs.gsoap" int ns__mymethod(xsd__string in, xsd__int *out); |
|
typedef char *xsd__string; typedef int xsd__int; |
The #include and #define directives are normally ignored by the gSOAP compiler. The use of the directives is enabled with the -i option of the gSOAP compiler, see Section 9.1. However, the gSOAP compiler will not actually parse the contents of the header files provided by the #include directives in a header file. Instead, the #include and #define directives will be added to the generated soapH.h header file before any other header file is included. Therefore, #include and #define directives can be used to control the C/C++ compilation process of the sources of an application.
The following example header file refers to ostream by including < ostream > :
|
#include < ostream > #define WITH_COOKIES // use HTTP cookie support (you must compile stdsoap2.cpp with -DWITH_COOKIES) #define WITH_OPENSSL // enable HTTPS (SSL) support (you must compile stdsoap2.cpp with -DWITH_OPENSSL) #define SOAP_DEFAULT_float FLT_NAN // use NaN instead of 0.0 extern class ostream; // ostream can't be (de)serialized, but need to be declared to make it visible to gSOAP class ns__myClass { ... virtual void print(ostream &s) const; // need ostream here ... }; |
Caution: Note that the use of #define in the header file does not automatically result in compiling stdsoap2.cpp with these directives. You MUST use the -DWITH_COOKIES and -DWITH_OPENSSL options when compiling stdsoap2.cpp before linking the object file with your codes. As an alternative, you can use #define WITH_SOAPDEFS_H and put the #define directives in the soapdefs.h file.
After invoking the gSOAP stub and skeleton compiler on a header file description of a service, the client application can be compiled on a Linux machine as follows:
| g++ -o myclient myclient.cpp stdsoap2.cpp soapC.cpp soapClient.cpp |
| g++ -o myclient myclient.cpp stdsoap2.cpp soapC.cpp soapClient.cpp -lsocket -lxnet -lnsl |
The myclient.cpp file must include soapH.h and must define a global namespace mapping table. A typical client program layout with namespace mapping table is shown below:
|
// Contents of file "myclient.cpp" #include "soapH.h"; ... // A remote method invocation: soap_call_some_remote_method(...); ... struct Namespace namespaces[] = { // {"ns-prefix", "ns-name"} {"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"}, {"ns1", "urn:my-remote-method"}, {NULL, NULL} }; ... |
After invoking the gSOAP stub and skeleton compiler on a header file description of the service, the server application can be compiled on a Linux machine as follows:
| g++ -o myserver myserver.cpp stdsoap2.cpp soapC.cpp soapServer.cpp |
| g++ -o myserver myserver.cpp stdsoap2.cpp soapC.cpp soapServer.cpp -lsocket -lxnet -lnsl |
The myserver.cpp file must include soapH.h and must define a global namespace mapping table. A typical service program layout with namespace mapping table is shown below:
|
// Contents of file "myserver.cpp" #include "soapH.h"; int main() { soap_serve(soap_new()); } ... // Implementations of the remote methods as C++ functions ... struct Namespace namespaces[] = { // {"ns-prefix", "ns-name"} {"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"}, {"ns1", "urn:my-remote-method"}, {NULL, NULL} }; ... |
The gSOAP compiler can be used to create pure C Web services and clients. The gSOAP stub and skeleton compiler
soapcpp2 generates .cpp files by default. The compiler generates .c files with the -c option.
However, these files only use C syntax and data types if the header
file input to soapcpp2 uses C syntax and data types. For example:
|
soapcpp2 -c quote.h gcc -o quote quote.c stdsoap2.c soapC.c soapClient.c |
gSOAP is SOAP 1.1 and SOAP 1.2 compliant and supports SOAP RPC and document/literal operations.
From the perspective of the C/C++ language, a few C++ language features are not supported by gSOAP and these features cannot be used in the specification of SOAP remote methods.
There are certain limitations for the following C++ language constructs:
The following macros (#defines) can be used to enable certain optional features:
|
gSOAP provides flags to control the input and output mode settings at runtime. These flags are divided into four categories: transport (IO), content encoding (ENC), XML marshalling (XML), and C/C++ data mapping (C).
Although gSOAP is fully SOAP 1.1 compliant, some SOAP implementations may have
trouble accepting multi-reference data and/or require explicit nil data so
these flags can be used to put gSOAP in ``safe mode''. In addition, the
embedding (or inlining) of multi-reference data is adopted in the SOAP 1.2
specification, which gSOAP automatically supports when handling with SOAP 1.2
messages. The flags are:
|
All flags are orthogonal, except SOAP_IO_FLUSH, SOAP_IO_BUFFER, SOAP_IO_STORE, and SOAP_IO_CHUNK which are enumerations and only one of these I/O flags can be used. Also the XML serialization flags SOAP_XML_TREE and SOAP_XML_GRAPH should not be mixed.
The flags control the inbound and outbound message transport, encoding, and
(de)serialization. The following functions are used to set and reset the flags
for input and output modes:
|
For example
|
struct soap soap; soap_init2(&soap, SOAP_IO_KEEPALIVE, SOAP_IO_KEEPALIVE | SOAP_ENC_ZLIB | SOAP_XML_TREE | SOAP_XML_CANONICAL); if (soap_call_ns__myMethod(&soap, ...)) ... |
In many cases, setting the input mode will have no effect, especially with HTTP transport because gSOAP will determine the optimal input buffering and the encoding used for an inbound message. The flags that have an effect on handling inbound messages are SOAP_IO_KEEPALIVE, SOAP_ENC_SSL (but automatic when "https:" endpoints are used or soap_ssl_accept), SOAP_C_NOIOB, SOAP_C_UTFSTRING, and SOAP_C_MBSTRING.
Caution: The SOAP_XML_TREE serialization flag can be used to improve interoperability with SOAP implementations that are not fully SOAP 1.1 compliant. However, a tree serialization will duplicate data when necessary and will crash the serializer for cyclic data structures.
Understanding gSOAP's run-time memory management is important to optimize client and service applications by eliminating memory leaks and/or dangling references.
There are two forms of dynamic (heap) allocations made by gSOAP's runtime for serialization and deserialization of data. Temporary data is created by the runtime such as hash tables to keep pointer reference information for serialization and hash tables to keep XML id/href information for multi-reference object deserialization. Deserialized data is created upon receiving SOAP messages. This data is stored on the heap and requires several calls to the malloc library function to allocate space for the data and new to create class instances. All such allocations are tracked by gSOAP's runtime by linked lists for later deallocation. The linked list for malloc allocations uses some extra space in each malloced block to form a chain of pointers through the malloced blocks. A separate malloced linked list is used to keep track of class instance allocations.
gSOAP does not enforce a deallocation policy and the user can adopt a deallocation policy that works best for a particular application. As a consequence, deserialized data is never deallocated by the gSOAP runtime unless the user explicitly forces deallocation by calling functions to deallocate data collectively or individually.
The deallocation functions are:
|
Individual data objects can be unlinked from the deallocation chain if necessary, to prevent deallocation by the collective soap_end or soap_destroy functions.
There are three situations to consider for memory deallocation policies for class instances:
To summarize, it is advised to pass class data types by pointer to a remote method. For example:
|
class X { ... }; ns__remoteMethod(X *in, ...); |
|
class X { ... }; class ns__remoteMethodResponse { ... }; ns__remoteMethod(X *in, ns__remoteMethodResponse &out); |
|
typedef int xsd__int; class X { ... }; class ArrayOfint { xsd__int *__ptr; int __size; }; ns__remoteMethod(X *in, ArrayOfint *out); |
|
typedef int xsd__int; class X { ... }; class ArrayOfint { xsd__int *__ptr; int __size; }; ns__remoteMethod(X *in, ArrayOfint *&out); |
|
Space allocated with soap_malloc will be released with the soap_end and soap_dealloc functions.
Objects instantiated with soap_new_X(struct soap*) are removed altogether with soap_destroy(struct soap*).
Individual objects instantiated with soap_new_X are removed with soap_delete_X(struct soap*, X*).
For example, the following service uses temporary data in the remote method implementation:
|
int main() { ... struct soap soap; soap_init(&soap); soap_serve(&soap); soap_end(&soap); ... } |
|
int ns__itoa(struct soap *soap, int i, char **a) { *a = (char*)soap_malloc(soap, 11); sprintf(*a, "%d", i); return SOAP_OK; } |
|
int ns__mymethod(...) { ... if (exception) { char *msg = (char*)soap_malloc(soap, 1024); // allocate temporary space for detailed message sprintf(msg, "...", ...); // produce the detailed message return soap_receiver_fault(soap, Än exception occurred", msg); // return the server-side fault } ... } |
gSOAP provides a function to duplicate a string into gSOAP's memory space:
| char *soap_strdup(struct soap *soap, const char *s) |
When a class declaration has a struct soap * field, this field will be set to point to the current gSOAP run-time environment by
gSOAP's deserializers and by the soap_new_Class functions.
This simplifies memory management for class instances.
The struct soap* pointer is implicitly set by the gSOAP deserializer for
the class or explicitly by calling the soap_new_X function for class X.
For example:
|
class Sample { public: struct soap *soap; // reference to gSOAP's run-time ... Sample(); ~Sample(); }; |
|
Sample::Sample() { this->soap = NULL; } Sample::~Sample() { soap_unlink(this->soap, this); } |
|
struct soap *soap = soap_new(); // new gSOAP runtime Sample *obj = soap_new_Sample(soap, -1); // new Sample object with obj->soap set to runtime ... delete obj; // also calls soap_unlink to remove obj from the deallocation chain soap_destroy(soap); // deallocate all (other) class instances soap_end(soap); // clean up |
|
class ns__myClass { ... struct soap *soap; // set by soap_new_ns__myClass() char *name; void setName(const char *s); ... }; |
|
int ns__myMethod(struct soap *soap, ...) { ns__myClass *p = soap_new_ns__myClass(soap, -1); p->setName("SOAP"); return SOAP_OK; } void ns__myClass::ns__setName(const char *s) { if (soap) name = (char*)soap_malloc(soap, strlen(s)+1); else name = (char*)malloc(strlen(s)+1); strcpy(name, s); } ns__myClass::ns__myClass() { soap = NULL; name = NULL; } ns__myClass::~ns__myClass() { if (!soap && name) free(name); soap_unlink(soap, this); } |
To activate message logging for debugging, un-comment the #define DEBUG directive in stdsoap2.h. Compile the client and/or
server applications as described above (or simply use g++ -DDEBUG ... to compile with debugging activated). When the client and server applications run, they will log their activity in three
separate files:
|
Caution: When installing a CGI application on the Web with debugging activated, the log files may sometimes not be created due to file access permission restrictions imposed on CGI applications. To get around this, create empty log files with universal write permissions. Be careful about the security implication of this.
You can test a service CGI application without deploying it on the Web.
To do this, create a client application for the service and activate message logging by this client.
Remove any old SENT.log file and run the client (which connects to the Web service or to another dummy, but valid address)
and copy the SENT.log file to another file, e.g. SENT.tst.
Then redirect the SENT.tst file to the service CGI application. For example,
| myservice.cgi < SENT.tst |
The file names of the log files and the logging activity can be controlled at the application level. This allows the creation of
separate log files by separate services, clients, and threads.
For example, the following service logs all SOAP messages (but no debug messages) in separate directories:
|
struct soap soap; soap_init(&soap); ... soap_set_recv_logfile(&soap, "logs/recv/service12.log"); // append all messages received in /logs/recv/service12.log soap_set_sent_logfile(&soap, "logs/sent/service12.log"); // append all messages sent in /logs/sent/service12.log soap_set_test_logfile(&soap, NULL); // no file name: do not save debug messages ... soap_serve(&soap); ... |
| g++ -o myclient myclient.cpp stdsoap2.cpp soapC.cpp soapClient.cpp -lsocket -lxnet -lnsl |
| g++ -o myclient myclient.cpp stdsoap2.cpp soapC.cpp soapClient.cpp -lsocket -lxnet -lnsl |
A SOAP remote method is specified as a C/C++ function prototype in a header file. The function is REQUIRED to return int, which is used to represent a SOAP error code, see Section 10.2. Multiple remote methods MAY be declared together in one header file.
The general form of a SOAP remote method specification is:
| [int] [namespace_prefix__]method_name([inparam1, inparam2, ...,] outparam); |
The method request is encoded in SOAP as an XML element and the namespace prefix, method name, and input parameters are encoded using the format:
|
<[namespace-prefix:]method_name xsi:type="[namespace-prefix:]method_name> <inparam-name1 xsi:type="...">...</inparam-name1> <inparam-name2 xsi:type="...">...</inparam-name2> ... </[namespace-prefix:]method_name> |
The XML response by the Web service is of the form:
|
<[namespace-prefix:]method-nameResponse xsi:type="[namespace-prefix:]method-nameResponse> <outparam-name xsi:type="...">...</outparam-name> </[namespace-prefix:]method-nameResponse> |
The gSOAP stub and skeleton compiler generates a stub routine for the remote
method. This stub is of the form:
| int soap_call_[namespace_prefix__]method_name(struct soap *soap, char *URL, char *action, [inparam1, inparam2, ...,] outparam); |
The gSOAP stub and skeleton compiler generates a skeleton routine for the
remote method. The skeleton function is:
| int soap_serve_[namespace_prefix__]method_name(struct soap *soap); |
The input parameters of a remote method MUST be passed by value. Input parameters cannot be passed by reference with the & reference operator, but an input parameter value MAY be passed by a pointer to the data. Of course, passing a pointer to the data is preferred when the size of the data of the parameter is large. Also, to pass instances of (derived) classes, pointers to the instance need to be used to avoid passing the instance by value which requires a temporary and prohibits passing derived class instances. When two input parameter values are identical, passing them using a pointer has the advantage that the value will be encoded only once as multi-reference (hence, the parameters are aliases). When input parameters are passed using a pointer, the data pointed to will not be modified by the remote method and returned to the caller.
The output parameter MUST be passed by reference using & or by using a pointer. Arrays are passed by reference by default and do not require the use of the reference operator &.
The input and output parameter types have certain limitations, see Section 9.10
If the output parameter is a struct or class type, it is considered a SOAP remote method response element instead of a simple output parameter value. That is, the name of the struct or class is the name of the response element and the struct or class fields are the output parameters of the remote method, see also 8.1.7. Hence, if the output parameter has to be a struct or class, a response struct or class MUST be declared as well. In addition, if a remote method returns multiple output parameters, a response struct or class MUST be declared. By convention, the response element is the remote method name ending with ``Response''.
The general form of a response element declaration is:
|
struct [namespace_prefix__]response_element_name { outparam1; outparam2; ... }; |
| [int] [namespace_prefix__]method_name([inparam1, inparam2, ...,] struct [namespace_prefix__]response_element_name {outparam1[, outparam2, ...]} &anyparam); |
The method request is encoded in SOAP as an independent element and the
namespace prefix, method name, and input parameters are encoded using the
format:
|
<[namespace-prefix:]method-name xsi:type="[namespace-prefix:]method-name> <inparam-name1 xsi:type="...">...</inparam-name1> <inparam-name2 xsi:type="...">...</inparam-name2> ... </[namespace-prefix:]method-name> |
The method response is expected to be of the form:
|
<[namespace-prefix:]response-element-name xsi:type="[namespace-prefix:]response-element-name> <outparam-name1 xsi:type="...">...</outparam-name1> <outparam-name2 xsi:type="...">...</outparam-name2> ... </[namespace-prefix:]response-element-name> |
The input and/or output parameters can be made anonymous, which allows the deserialization of requests/responses with different parameter names as is endorsed by the SOAP 1.1 specification, see Section 8.1.13.
The error codes returned by the stub and skeleton routines are listed below.
|
A remote method implemented in a SOAP service MUST return an error code as the function's return value. SOAP_OK denotes success and SOAP_FAULT denotes an exception. The exception details can be assigned with the soap_receiver_fault(struct soap *soap, const char *faultstring, const char *detail) which sets the strings soap.fault->faultstring and soap.fault->detail for SOAP 1.1, and soap.fault->SOAP_ENV__Reason and soap.fault->SOAP_ENV__Detail for SOAP 1.2, where soap is a variable that contains the current runtime environment, see Section 12. A receiver error indicates that the service can't handle the request, but can possibly recover from the error. To return an unrecoverable error, use soap_receiver_fault(struct soap *soap, const char *faultstring, const char *detail).
To return a HTTP error code a service method can simply return the HTTP error code number.
For example, return 404; returns a "404 Not Found" HTTP error back to the client. The soap.error
is set to the HTTP error code at the client side.
The HTTP 1.1 error codes are:
|
One of the ``secrets'' behind the power and flexibility of gSOAP's encoding and
decoding of remote method names, class names, type identifiers, and struct or
class fields is the ability to specify namespace prefixes with these names that
are used to denote their encoding style. More specifically, a C/C++ identifier
name of the form
| [namespace_prefix__]element_name |
| <[namespace-prefix:]element-name ...> |
XML element names are NCNames (restricted strings) that MAY contain
hyphens, dots, and underscores. The special characters in the XML
element names of remote methods, structs, classes, typedefs, and fields can be
controlled using the following conventions: A single underscore in a
namespace prefix or identifier name is replaced by a hyphen (-) in the
XML element name. For example, the identifier name SOAP_ENC__ur_type
is represented in XML as SOAP-ENC:ur-type. The sequence _DOT is
replaced by a dot (.), and the sequence _USCORE is replaced by
an underscore (_) in the corresponding XML element name. For example:
|
class n_s__biz_DOTcom { char *n_s__biz_USCOREname; }; |
|
<n-s:biz.com xsi:type="n-s:biz.com"> <n-s:biz_name xsi:type="string">Bizybiz</n-s:biz_name> </n-s:biz.com> |
For decoding, the underscores in identifier names act as wildcards. An XML element is parsed and matches the name of an identifier if the name is identical to the element name (case insensitive) and the underscores in the identifier name are allowed to match any character in the element name. For example, the identifier name I_want__soap_fun_the_bea___DOTcom matches the element name I-want:SOAP4fun@the-beach.com.
A namespace mapping table MUST be defined by clients and service applications.
The mapping table is used by the serializers and deserializers of the stub and
skeleton routines to produce a valid SOAP payload and to validate an incoming
SOAP payload. A typical mapping table is shown below:
|
struct Namespace namespaces[] = { // {"ns-prefix", "ns-name"} {"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/"}, // MUST be first {"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"}, // MUST be second {"xsi", "http://www.w3.org/2001/XMLSchema-instance"}, // MUST be third {"xsd", "http://www.w3.org/2001/XMLSchema"}, // Required for XML schema types {"ns1", "urn:my-service-URI"}, // The namespace URI of the remote methods {NULL, NULL} // end of table }; |
An optional namespace pattern MAY be provided with each namespace mapping table
entry. The patterns provide an alternative namespace matching for the
validation of decoded SOAP messages. In this pattern, dashes (-) are
single-character wildcards and asterisks (*) are multi-character
wildcards. For example, to decode different versions of XML Schema type with
different authoring dates, four dashes can be used in place of the specific
dates in the namespace mapping table pattern:
|
struct Namespace namespaces[] = { // {"ns-prefix", "ns-name", "ns-name validation pattern"} ... {"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/----/XMLSchema-instance"}, {"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/----/XMLSchema"}, ... |
|
struct Namespace namespaces[] = { // {"ns-prefix", "ns-name", "ns-name validation pattern"} ... {"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance"}, {"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema"}, ... |
For decoding elements with namespace prefixes, the namespace URI associated with the namespace prefix (through the xmlns attribute of an XML element) is searched from the beginning to the end in a namespace mapping table, and for every row the following tests are performed as part of the validation process:
For example, let's say we have the following structs:
|
struct a__elt { ... }; struct b__elt { ... }; struct k__elt { ... }; |
|
struct Namespace namespaces[] = { // {"ns-prefix", "ns-name", "ns-name validation pattern"} ... {"a", "some uri"}, {"b", "other uri"}, {"c", "his uri", "* uri"}, ... |
|
<n:elt xmlns:n="some URI"> matches the struct name a__elt ... <m:elt xmlns:m="other URI"> matches the struct name b__elt ... <k:elt xmlns:k="my URI"> matches the struct name c__elt ... |
It is possible to use a number of different namespace tables and select the one that is appropriate.
For example, an application might contact many different Web services all using different namespace URIs.
If all the URIs are stored in one table, each remote method invocation will dump the whole namespace
table in the SOAP payload. There is no technical problem with that, but it can be ugly when the table is large.
To use different namespace tables, declare a pointer to a table and set the pointer to a particular table before remote method
invocation. For example:
|
struct Namespace namespacesTable1[] = { ... }; struct Namespace namespacesTable2[] = { ... }; struct Namespace namespacesTable3[] = { ... }; struct Namespace *namespaces; ... struct soap soap; ... soap_init(&soap); soap_set_namespaces(&soap, namespaceTable1); soap_call_remote_method(&soap, URL, Action, ...); ... |
This section describes the serialization and deserialization of C and C++ data types for SOAP 1.1 and 1.2 compliant encoding and decoding.
The default encoding rules for the primitive C and C++ data types are given in the table below:
|
By default, encoding of the primitive types will take place as per SOAP encoding style. The encoding can be changed to any XML schema type (XSD type) with an optional namespace prefix by using a typedef in the header file input to the gSOAP stub and skeleton compiler. The declaration enables the implementation of built-in XML schema types (also known as XSD types) such as positiveInteger, xsd:anyURI, and xsd:date for which no built-in data structures in C and C++ exist but which can be represented using standard data structures such as strings, integers, and floats.
The typedef declaration is frequently used for convenience in C. A typedef declares a type name for a (complex) type expression. The type name can then be used in other declarations in place of the more complex type expression, which often improves the readability of the program code.
The gSOAP compiler interprets typedef declarations the same way as a regular C compiler interprets them, i.e. as types in declarations. In addition however, the gSOAP compiler will also use the type name in the encoding of the data in SOAP. The typedef name will appear as the XML element name of an independent element and as the value of the xsi:type attribute in the SOAP payload.
Many built-in primitive and derived XSD types such as xsd:anyURI, positiveInteger, and decimal can be stored by standard primitive data structures in C++ as well such as strings, integers, floats, and doubles. To serialize strings, integers, floats, and doubles as built-in primitive and derived XSD types. To this end, a typedef declaration can be used to declare an XSD type.
For example, the declaration
| typedef unsigned int xsd__positiveInteger; |
| <positiveInteger xsi:type="xsd:positiveInteger">3</positiveInteger> |
The built-in primitive and derived numerical XML Schema types are listed below together with their recommended typedef declarations. Note that the SOAP encoding schemas for primitive types are derived from the built-in XML schema types, so SOAP_ENC__ can be used as a namespace prefix instead of xsd__.
| typedef char *xsd__anyURI; |
| typedef char *xsd__base64Binary; |
| typedef bool xsd__boolean; |
| <xsd:boolean xsi:type="xsd:boolean">...</xsd:boolean> |
| typedef char xsd__byte; |
| <xsd:byte xsi:type="xsd:byte">...</xsd:byte> |
| typedef time_t xsd__dateTime; |
Strings (char*) can be used to store xsd:dateTime XML schema types. The type declaration is:
| typedef char *xsd__dateTime; |
| typedef char *xsd__date; |
| typedef double xsd__decimal; |
| <xsd:double xsi:type="xsd:decimal">...</xsd:double> |
| typedef double xsd__double; |
| <xsd:double xsi:type="xsd:double">...</xsd:double> |
| typedef char *xsd__duration; |
| typedef float xsd__float; |
| <xsd:float xsi:type="xsd:float">...</xsd:float> |
| typedef char *xsd__hexBinary; |
| typedef int xsd__int; |
| typedef long xsd__int; |
| <xsd:int xsi:type="xsd:int">...</xsd:int> |
| typedef long long xsd__integer; |
| <xsd:integer xsi:type="xsd:integer">...</xsd:integer> |
It is possible to use a number of different namespace tables and select the one that is appropriate.
For example, an application might contact many different Web services all using different namespace URIs.
If all the URIs are stored in one table, each remote method invocation will dump the whole namespace
table in the SOAP payload. There is no technical problem with that, but it can be ugly when the table is large.
To use different namespace tables, declare a pointer to a table and set the pointer to a particular table before remote method
invocation. For example:
|
struct Namespace namespacesTable1[] = { ... }; struct Namespace namespacesTable2[] = { ... }; struct Namespace namespacesTable3[] = { ... }; struct Namespace *namespaces; ... struct soap soap; ... soap_init(&soap); soap_set_namespaces(&soap, namespaceTable1); soap_call_remote_method(&soap, URL, Action, ...); ... |
This section describes the serialization and deserialization of C and C++ data types for SOAP 1.1 and 1.2 compliant encoding and decoding.
The default encoding rules for the primitive C and C++ data types are given in the table below:
|
By default, encoding of the primitive types will take place as per SOAP encoding style. The encoding can be changed to any XML schema type (XSD type) with an optional namespace prefix by using a typedef in the header file input to the gSOAP stub and skeleton compiler. The declaration enables the implementation of built-in XML schema types (also known as XSD types) such as positiveInteger, xsd:anyURI, and xsd:date for which no built-in data structures in C and C++ exist but which can be represented using standard data structures such as strings, integers, and floats.
The typedef declaration is frequently used for convenience in C. A typedef declares a type name for a (complex) type expression. The type name can then be used in other declarations in place of the more complex type expression, which often improves the readability of the program code.
The gSOAP compiler interprets typedef declarations the same way as a regular C compiler interprets them, i.e. as types in declarations. In addition however, the gSOAP compiler will also use the type name in the encoding of the data in SOAP. The typedef name will appear as the XML element name of an independent element and as the value of the xsi:type attribute in the SOAP payload.
Many built-in primitive and derived XSD types such as xsd:anyURI, positiveInteger, and decimal can be stored by standard primitive data structures in C++ as well such as strings, integers, floats, and doubles. To serialize strings, integers, floats, and doubles as built-in primitive and derived XSD types. To this end, a typedef declaration can be used to declare an XSD type.
For example, the declaration
| typedef unsigned int xsd__positiveInteger; |
| <positiveInteger xsi:type="xsd:positiveInteger">3</positiveInteger> |
The built-in primitive and derived numerical XML Schema types are listed below together with their recommended typedef declarations. Note that the SOAP encoding schemas for primitive types are derived from the built-in XML schema types, so SOAP_ENC__ can be used as a namespace prefix instead of xsd__.
| typedef char *xsd__anyURI; |
| typedef char *xsd__base64Binary; |
| typedef bool xsd__boolean; |
| <xsd:boolean xsi:type="xsd:boolean">...</xsd:boolean> |
| typedef char xsd__byte; |
| <xsd:byte xsi:type="xsd:byte">...</xsd:byte> |
| typedef time_t xsd__dateTime; |
Strings (char*) can be used to store xsd:dateTime XML schema types. The type declaration is:
| typedef char *xsd__dateTime; |
| typedef char *xsd__date; |
| typedef double xsd__decimal; |
| <xsd:double xsi:type="xsd:decimal">...</xsd:double> |
| typedef double xsd__double; |
| <xsd:double xsi:type="xsd:double">...</xsd:double> |
| typedef char *xsd__duration; |
| typedef float xsd__float; |
| <xsd:float xsi:type="xsd:float">...</xsd:float> |
| typedef char *xsd__hexBinary; |
| typedef int xsd__int; |
| typedef long xsd__int; |
| <xsd:int xsi:type="xsd:int">...</xsd:int> |
| typedef long long xsd__integer; |
| <xsd:integer xsi:type="xsd:integer">...</xsd:integer> |
It is possible to use a number of different namespace tables and select the one that is appropriate.
For example, an application might contact many different Web services all using different namespace URIs.
If all the URIs are stored in one table, each remote method invocation will dump the whole namespace
table in the SOAP payload. There is no technical problem with that, but it can be ugly when the table is large.
To use different namespace tables, declare a pointer to a table and set the pointer to a particular table before remote method
invocation. For example:
|
struct Namespace namespacesTable1[] = { ... }; struct Namespace namespacesTable2[] = { ... }; struct Namespace namespacesTable3[] = { ... }; struct Namespace *namespaces; ... struct soap soap; ... soap_init(&soap); soap_set_namespaces(&soap, namespaceTable1); soap_call_remote_method(&soap, URL, Action, ...); ... |
This section describes the serialization and deserialization of C and C++ data types for SOAP 1.1 and 1.2 compliant encoding and decoding.
The default encoding rules for the primitive C and C++ data types are given in the table below:
|
By default, encoding of the primitive types will take place as per SOAP encoding style. The encoding can be changed to any XML schema type (XSD type) with an optional namespace prefix by using a typedef in the header file input to the gSOAP stub and skeleton compiler. The declaration enables the implementation of built-in XML schema types (also known as XSD types) such as positiveInteger, xsd:anyURI, and xsd:date for which no built-in data structures in C and C++ exist but which can be represented using standard data structures such as strings, integers, and floats.
The typedef declaration is frequently used for convenience in C. A typedef declares a type name for a (complex) type expression. The type name can then be used in other declarations in place of the more complex type expression, which often improves the readability of the program code.
The gSOAP compiler interprets typedef declarations the same way as a regular C compiler interprets them, i.e. as types in declarations. In addition however, the gSOAP compiler will also use the type name in the encoding of the data in SOAP. The typedef name will appear as the XML element name of an independent element and as the value of the xsi:type attribute in the SOAP payload.
Many built-in primitive and derived XSD types such as xsd:anyURI, positiveInteger, and decimal can be stored by standard primitive data structures in C++ as well such as strings, integers, floats, and doubles. To serialize strings, integers, floats, and doubles as built-in primitive and derived XSD types. To this end, a typedef declaration can be used to declare an XSD type.
For example, the declaration
| typedef unsigned int xsd__positiveInteger; |
| <positiveInteger xsi:type="xsd:positiveInteger">3</positiveInteger> |
The built-in primitive and derived numerical XML Schema types are listed below together with their recommended typedef declarations. Note that the SOAP encoding schemas for primitive types are derived from the built-in XML schema types, so SOAP_ENC__ can be used as a namespace prefix instead of xsd__.
| typedef char *xsd__anyURI; |
| typedef char *xsd__base64Binary; |
| typedef bool xsd__boolean; |
| <xsd:boolean xsi:type="xsd:boolean">...</xsd:boolean> |
| typedef char xsd__byte; |
| <xsd:byte xsi:type="xsd:byte">...</xsd:byte> |
| typedef time_t xsd__dateTime; |
Strings (char*) can be used to store xsd:dateTime XML schema types. The type declaration is:
| typedef char *xsd__dateTime; |
| typedef char *xsd__date; |
| typedef double xsd__decimal; |
| <xsd:double xsi:type="xsd:decimal">...</xsd:double> |
| typedef double xsd__double; |
| <xsd:double xsi:type="xsd:double">...</xsd:double> |
| typedef char *xsd__duration; |
| typedef float xsd__float; |
| <xsd:float xsi:type="xsd:float">...</xsd:float> |
| typedef char *xsd__hexBinary; |
| typedef int xsd__int; |
| typedef long xsd__int; |
| <xsd:int xsi:type="xsd:int">...</xsd:int> |
| typedef long long xsd__integer; |
| <xsd:integer xsi:type="xsd:integer">...</xsd:integer> |
It is possible to use a number of different namespace tables and select the one that is appropriate.
For example, an application might contact many different Web services all using different namespace URIs.
If all the URIs are stored in one table, each remote method invocation will dump the whole namespace
table in the SOAP payload. There is no technical problem with that, but it can be ugly when the table is large.
To use different namespace tables, declare a pointer to a table and set the pointer to a particular table before remote method
invocation. For example:
|
struct Namespace namespacesTable1[] = { ... }; struct Namespace namespacesTable2[] = { ... }; struct Namespace namespacesTable3[] = { ... }; struct Namespace *namespaces; ... struct soap soap; ... soap_init(&soap); soap_set_namespaces(&soap, namespaceTable1); soap_call_remote_method(&soap, URL, Action, ...); ... |
This section describes the serialization and deserialization of C and C++ data types for SOAP 1.1 and 1.2 compliant encoding and decoding.
The default encoding rules for the primitive C and C++ data types are given in the table below:
|
By default, encoding of the primitive types will take place as per SOAP encoding style. The encoding can be changed to any XML schema type (XSD type) with an optional namespace prefix by using a typedef in the header file input to the gSOAP stub and skeleton compiler. The declaration enables the implementation of built-in XML schema types (also known as XSD types) such as positiveInteger, xsd:anyURI, and xsd:date for which no built-in data structures in C and C++ exist but which can be represented using standard data structures such as strings, integers, and floats.
The typedef declaration is frequently used for convenience in C. A typedef declares a type name for a (complex) type expression. The type name can then be used in other declarations in place of the more complex type expression, which often improves the readability of the program code.
The gSOAP compiler interprets typedef declarations the same way as a regular C compiler interprets them, i.e. as types in declarations. In addition however, the gSOAP compiler will also use the type name in the encoding of the data in SOAP. The typedef name will appear as the XML element name of an independent element and as the value of the xsi:type attribute in the SOAP payload.
Many built-in primitive and derived XSD types such as xsd:anyURI, positiveInteger, and decimal can be stored by standard primitive data structures in C++ as well such as strings, integers, floats, and doubles. To serialize strings, integers, floats, and doubles as built-in primitive and derived XSD types. To this end, a typedef declaration can be used to declare an XSD type.
For example, the declaration
| typedef unsigned int xsd__positiveInteger; |
| <positiveInteger xsi:type="xsd:positiveInteger">3</positiveInteger> |
The built-in primitive and derived numerical XML Schema types are listed below together with their recommended typedef declarations. Note that the SOAP encoding schemas for primitive types are derived from the built-in XML schema types, so SOAP_ENC__ can be used as a namespace prefix instead of xsd__.
| typedef char *xsd__anyURI; |
| typedef char *xsd__base64Binary; |
| typedef bool xsd__boolean; |
| <xsd:boolean xsi:type="xsd:boolean">...</xsd:boolean> |
| typedef char xsd__byte; |
| <xsd:byte xsi:type="xsd:byte">...</xsd:byte> |
| typedef time_t xsd__dateTime; |
Strings (char*) can be used to store xsd:dateTime XML schema types. The type declaration is:
| typedef char *xsd__dateTime; |
| typedef char *xsd__date; |
| typedef double xsd__decimal; |
| <xsd:double xsi:type="xsd:decimal">...</xsd:double> |
| typedef double xsd__double; |
| <xsd:double xsi:type="xsd:double">...</xsd:double> |
| typedef char *xsd__duration; |
| typedef float xsd__float; |
| <xsd:float xsi:type="xsd:float">...</xsd:float> |
| typedef char *xsd__hexBinary; |
| typedef int xsd__int; |
| typedef long xsd__int; |
| <xsd:int xsi:type="xsd:int">...</xsd:int> |
| typedef long long xsd__integer; |
| <xsd:integer xsi:type="xsd:integer">...</xsd:integer> |
It is possible to use a number of different namespace tables and select the one that is appropriate.
For example, an application might contact many different Web services all using different namespace URIs.
If all the URIs are stored in one table, each remote method invocation will dump the whole namespace
table in the SOAP payload. There is no technical problem with that, but it can be ugly when the table is large.
To use different namespace tables, declare a pointer to a table and set the pointer to a particular table before remote method
invocation. For example:
|
struct Namespace namespacesTable1[] = { ... }; struct Namespace namespacesTable2[] = { ... }; struct Namespace namespacesTable3[] = { ... }; struct Namespace *namespaces; ... struct soap soap; ... soap_init(&soap); soap_set_namespaces(&soap, namespaceTable1); soap_call_remote_method(&soap, URL, Action, ...); ... |
This section describes the serialization and deserialization of C and C++ data types for SOAP 1.1 and 1.2 compliant encoding and decoding.
The default encoding rules for the primitive C and C++ data types are given in the table below:
|
By default, encoding of the primitive types will take place as per SOAP encoding style. The encoding can be changed to any XML schema type (XSD type) with an optional namespace prefix by using a typedef in the header file input to the gSOAP stub and skeleton compiler. The declaration enables the implementation of built-in XML schema types (also known as XSD types) such as positiveInteger, xsd:anyURI, and xsd:date for which no built-in data structures in C and C++ exist but which can be represented using standard data structures such as strings, integers, and floats.
The typedef declaration is frequently used for convenience in C. A typedef declares a type name for a (complex) type expression. The type name can then be used in other declarations in place of the more complex type expression, which often improves the readability of the program code.
The gSOAP compiler interprets typedef declarations the same way as a regular C compiler interprets them, i.e. as types in declarations. In addition however, the gSOAP compiler will also use the type name in the encoding of the data in SOAP. The typedef name will appear as the XML element name of an independent element and as the value of the xsi:type attribute in the SOAP payload.
Many built-in primitive and derived XSD types such as xsd:anyURI, positiveInteger, and decimal can be stored by standard primitive data structures in C++ as well such as strings, integers, floats, and doubles. To serialize strings, integers, floats, and doubles as built-in primitive and derived XSD types. To this end, a typedef declaration can be used to declare an XSD type.
For example, the declaration
| typedef unsigned int xsd__positiveInteger; |
| <positiveInteger xsi:type="xsd:positiveInteger">3</positiveInteger> |
The built-in primitive and derived numerical XML Schema types are listed below together with their recommended typedef declarations. Note that the SOAP encoding schemas for primitive types are derived from the built-in XML schema types, so SOAP_ENC__ can be used as a namespace prefix instead of xsd__.
| typedef char *xsd__anyURI; |
| typedef char *xsd__base64Binary; |
| typedef bool xsd__boolean; |
| <xsd:boolean xsi:type="xsd:boolean">...</xsd:boolean> |
| typedef char xsd__byte; |
| <xsd:byte xsi:type="xsd:byte">...</xsd:byte> |
| typedef time_t xsd__dateTime; |
Strings (char*) can be used to store xsd:dateTime XML schema types. The type declaration is:
| typedef char *xsd__dateTime; |
| typedef char *xsd__date; |
| typedef double xsd__decimal; |
| <xsd:double xsi:type="xsd:decimal">...</xsd:double> |
| typedef double xsd__double; |
| <xsd:double xsi:type="xsd:double">...</xsd:double> |
| typedef char *xsd__duration; |
| typedef float xsd__float; |
| <xsd:float xsi:type="xsd:float">...</xsd:float> |
| typedef char *xsd__hexBinary; |
| typedef int xsd__int; |
| typedef long xsd__int; |
| <xsd:int xsi:type="xsd:int">...</xsd:int> |
| typedef long long xsd__integer; |
| <xsd:integer xsi:type="xsd:integer">...</xsd:integer> |
It is possible to use a number of different namespace tables and select the one that is appropriate.
For example, an application might contact many different Web services all using different namespace URIs.
If all the URIs are stored in one table, each remote method invocation will dump the whole namespace
table in the SOAP payload. There is no technical problem with that, but it can be ugly when the table is large.
To use different namespace tables, declare a pointer to a table and set the pointer to a particular table before remote method
invocation. For example:
|
struct Namespace namespacesTable1[] = { ... }; struct Namespace namespacesTable2[] = { ... }; struct Namespace namespacesTable3[] = { ... }; struct Namespace *namespaces; ... struct soap soap; ... soap_init(&soap); soap_set_namespaces(&soap, namespaceTable1); soap_call_remote_method(&soap, URL, Action, ...); ... |
This section describes the serialization and deserialization of C and C++ data types for SOAP 1.1 and 1.2 compliant encoding and decoding.
The default encoding rules for the primitive C and C++ data types are given in the table below:
|
By default, encoding of the primitive types will take place as per SOAP encoding style. The encoding can be changed to any XML schema type (XSD type) with an optional namespace prefix by using a typedef in the header file input to the gSOAP stub and skeleton compiler. The declaration enables the implementation of built-in XML schema types (also known as XSD types) such as positiveInteger, xsd:anyURI, and xsd:date for which no built-in data structures in C and C++ exist but which can be represented using standard data structures such as strings, integers, and floats.
The typedef declaration is frequently used for convenience in C. A typedef declares a type name for a (complex) type expression. The type name can then be used in other declarations in place of the more complex type expression, which often improves the readability of the program code.
The gSOAP compiler interprets typedef declarations the same way as a regular C compiler interprets them, i.e. as types in declarations. In addition however, the gSOAP compiler will also use the type name in the encoding of the data in SOAP. The typedef name will appear as the XML element name of an independent element and as the value of the xsi:type attribute in the SOAP payload.
Many built-in primitive and derived XSD types such as xsd:anyURI, positiveInteger, and decimal can be stored by standard primitive data structures in C++ as well such as strings, integers, floats, and doubles. To serialize strings, integers, floats, and doubles as built-in primitive and derived XSD types. To this end, a typedef declaration can be used to declare an XSD type.
For example, the declaration
| typedef unsigned int xsd__positiveInteger; |
| <positiveInteger xsi:type="xsd:positiveInteger">3</positiveInteger> |
The built-in primitive and derived numerical XML Schema types are listed below together with their recommended typedef declarations. Note that the SOAP encoding schemas for primitive types are derived from the built-in XML schema types, so SOAP_ENC__ can be used as a namespace prefix instead of xsd__.
| typedef char *xsd__anyURI; |
| typedef char *xsd__base64Binary; |
| typedef bool xsd__boolean; |
| <xsd:boolean xsi:type="xsd:boolean">...</xsd:boolean> |
| typedef char xsd__byte; |
| <xsd:byte xsi:type="xsd:byte">...</xsd:byte> |
| typedef time_t xsd__dateTime; |
Strings (char*) can be used to store xsd:dateTime XML schema types. The type declaration is:
| typedef char *xsd__dateTime; |
| typedef char *xsd__date; |
| typedef double xsd__decimal; |
| <xsd:double xsi:type="xsd:decimal">...</xsd:double> |
| typedef double xsd__double; |
| <xsd:double xsi:type="xsd:double">...</xsd:double> |
| typedef char *xsd__duration; |
| typedef float xsd__float; |
| <xsd:float xsi:type="xsd:float">...</xsd:float> |
| typedef char *xsd__hexBinary; |
| typedef int xsd__int; |
| typedef long xsd__int; |
| <xsd:int xsi:type="xsd:int">...</xsd:int> |
| typedef long long xsd__integer; |
| <xsd:integer xsi:type="xsd:integer">...</xsd:integer> |
It is possible to use a number of different namespace tables and select the one that is appropriate.
For example, an application might contact many different Web services all using different namespace URIs.
If all the URIs are stored in one table, each remote method invocation will dump the whole namespace
table in the SOAP payload. There is no technical problem with that, but it can be ugly when the table is large.
To use different namespace tables, declare a pointer to a table and set the pointer to a particular table before remote method
invocation. For example:
|
struct Namespace namespacesTable1[] = { ... }; struct Namespace namespacesTable2[] = { ... }; struct Namespace namespacesTable3[] = { ... }; struct Namespace *namespaces; ... struct soap soap; ... soap_init(&soap); soap_set_namespaces(&soap, namespaceTable1); soap_call_remote_method(&soap, URL, Action, ...); ... |
This section describes the serialization and deserialization of C and C++ data types for SOAP 1.1 and 1.2 compliant encoding and decoding.
The default encoding rules for the primitive C and C++ data types are given in the table below:
|
By default, encoding of the primitive types will take place as per SOAP encoding style. The encoding can be changed to any XML schema type (XSD type) with an optional namespace prefix by using a typedef in the header file input to the gSOAP stub and skeleton compiler. The declaration enables the implementation of built-in XML schema types (also known as XSD types) such as positiveInteger, xsd:anyURI, and xsd:date for which no built-in data structures in C and C++ exist but which can be represented using standard data structures such as strings, integers, and floats.
The typedef declaration is frequently used for convenience in C. A typedef declares a type name for a (complex) type expression. The type name can then be used in other declarations in place of the more complex type expression, which often improves the readability of the program code.
The gSOAP compiler interprets typedef declarations the same way as a regular C compiler interprets them, i.e. as types in declarations. In addition however, the gSOAP compiler will also use the type name in the encoding of the data in SOAP. The typedef name will appear as the XML element name of an independent element and as the value of the xsi:type attribute in the SOAP payload.
Many built-in primitive and derived XSD types such as xsd:anyURI, positiveInteger, and decimal can be stored by standard primitive data structures in C++ as well such as strings, integers, floats, and doubles. To serialize strings, integers, floats, and doubles as built-in primitive and derived XSD types. To this end, a typedef declaration can be used to declare an XSD type.
For example, the declaration
| typedef unsigned int xsd__positiveInteger; |
| <positiveInteger xsi:type="xsd:positiveInteger">3</positiveInteger> |
The built-in primitive and derived numerical XML Schema types are listed below together with their recommended typedef declarations. Note that the SOAP encoding schemas for primitive types are derived from the built-in XML schema types, so SOAP_ENC__ can be used as a namespace prefix instead of xsd__.
| typedef char *xsd__anyURI; |
| typedef char *xsd__base64Binary; |
| typedef bool xsd__boolean; |
| <xsd:boolean xsi:type="xsd:boolean">...</xsd:boolean> |
| typedef char xsd__byte; |
| <xsd:byte xsi:type="xsd:byte">...</xsd:byte> |
| typedef time_t xsd__dateTime; |
Strings (char*) can be used to store xsd:dateTime XML schema types. The type declaration is:
| typedef char *xsd__dateTime; |
| typedef char *xsd__date; |
| typedef double xsd__decimal; |
| <xsd:double xsi:type="xsd:decimal">...</xsd:double> |
| typedef double xsd__double; |
| <xsd:double xsi:type="xsd:double">...</xsd:double> |
| typedef char *xsd__duration; |
| typedef float xsd__float; |
| <xsd:float xsi:type="xsd:float">...</xsd:float> |
| typedef char *xsd__hexBinary; |
| typedef int xsd__int; |
| typedef long xsd__int; |
| <xsd:int xsi:type="xsd:int">...</xsd:int> |
| typedef long long xsd__integer; |
| <xsd:integer xsi:type="xsd:integer">...</xsd:integer> |
It is possible to use a number of different namespace tables and select the one that is appropriate.
For example, an application might contact many different Web services all using different namespace URIs.
If all the URIs are stored in one table, each remote method invocation will dump the whole namespace
table in the SOAP payload. There is no technical problem with that, but it can be ugly when the table is large.
To use different namespace tables, declare a pointer to a table and set the pointer to a particular table before remote method
invocation. For example:
|
struct Namespace namespacesTable1[] = { ... }; struct Namespace namespacesTable2[] = { ... }; struct Namespace namespacesTable3[] = { ... }; struct Namespace *namespaces; ... struct soap soap; ... soap_init(&soap); soap_set_namespaces(&soap, namespaceTable1); soap_call_remote_method(&soap, URL, Action, ...); ... |
This section describes the serialization and deserialization of C and C++ data types for SOAP 1.1 and 1.2 compliant encoding and decoding.
The default encoding rules for the primitive C and C++ data types are given in the table below:
|
By default, encoding of the primitive types will take place as per SOAP encoding style. The encoding can be changed to any XML schema type (XSD type) with an optional namespace prefix by using a typedef in the header file input to the gSOAP stub and skeleton compiler. The declaration enables the implementation of built-in XML schema types (also known as XSD types) such as positiveInteger, xsd:anyURI, and xsd:date for which no built-in data structures in C and C++ exist but which can be represented using standard data structures such as strings, integers, and floats.
The typedef declaration is frequently used for convenience in C. A typedef declares a type name for a (complex) type expression. The type name can then be used in other declarations in place of the more complex type expression, which often improves the readability of the program code.
The gSOAP compiler interprets typedef declarations the same way as a regular C compiler interprets them, i.e. as types in declarations. In addition however, the gSOAP compiler will also use the type name in the encoding of the data in SOAP. The typedef name will appear as the XML element name of an independent element and as the value of the xsi:type attribute in the SOAP payload.
Many built-in primitive and derived XSD types such as xsd:anyURI, positiveInteger, and decimal can be stored by standard primitive data structures in C++ as well such as strings, integers, floats, and doubles. To serialize strings, integers, floats, and doubles as built-in primitive and derived XSD types. To this end, a typedef declaration can be used to declare an XSD type.
For example, the declaration
| typedef unsigned int xsd__positiveInteger; |
| <positiveInteger xsi:type="xsd:positiveInteger">3</positiveInteger> |
The built-in primitive and derived numerical XML Schema types are listed below together with their recommended typedef declarations. Note that the SOAP encoding schemas for primitive types are derived from the built-in XML schema types, so SOAP_ENC__ can be used as a namespace prefix instead of xsd__.
| typedef char *xsd__anyURI; |
| typedef char *xsd__base64Binary; |
| typedef bool xsd__boolean; |
| <xsd:boolean xsi:type="xsd:boolean">...</xsd:boolean> |
| typedef char xsd__byte; |
| <xsd:byte xsi:type="xsd:byte">...</xsd:byte> |
| typedef time_t xsd__dateTime; |
Strings (char*) can be used to store xsd:dateTime XML schema types. The type declaration is:
| typedef char *xsd__dateTime; |
| typedef char *xsd__date; |
| typedef double xsd__decimal; |
| <xsd:double xsi:type="xsd:decimal">...</xsd:double> |
| typedef double xsd__double; |
| <xsd:double xsi:type="xsd:double">...</xsd:double> |
| typedef char *xsd__duration; |
| typedef float xsd__float; |
| <xsd:float xsi:type="xsd:float">...</xsd:float> |
| typedef char *xsd__hexBinary; |
| typedef int xsd__int; |
| typedef long xsd__int; |
| <xsd:int xsi:type="xsd:int">...</xsd:int> |
| typedef long long xsd__integer; |
| <xsd:integer xsi:type="xsd:integer">...</xsd:integer> |