Genivia Home Documentation
gSOAP user guide

updated Tue Aug 27 2024 by Robert van Engelen
 
gSOAP user guide

Table of Contents

User guide

Copyright (c) 2000,2024, Genivia Inc.
All rights reserved.

Introduction

The gSOAP toolkit offers C/C++ tools and libraries to implement efficient and secure SOAP, XML, JSON and REST client and service Web API applications. The tools also offer XML data bindings for C and C++ to generate XML serializers to efficiently read and write C/C++ data to and from files and streams.

The gSOAP wsdl2h tool consumes WSDL and XSD schema files to converts them to C/C++ source code to implement XML messaging infrastructures. This frees the developer to focus on application functionality rather than on infrastructure.

More specifically, the wsdl2h tool consumes WSDLs to generate a C or C++ interface header file, which uses a developer-friendly C/C++ header file syntax. This allows developers to inspect Web services and XML schemas from a functionality point of view, rather than getting bogged down into the underlying SOAP-based infrastructure details of WSDLs and XSDs.

The soapcpp2 tool generates all the Web service binding source code with XML serializers necessary to quickly develop server-side and client-side Web service APIs.

The soapcpp2 tool can also be used to produce, rather than consume, WSDL and XSD files to deploy XML Web services or to develop XML applications. This approach allows the deployment of legacy C/C++ applications as services. Simply describe the Web API in a C or C++ interface header file for the soapcpp2 tool to generate the C/C++ source code that glues everything together.

Besides SOAP-based services, also non-SOAP XML and JSON REST services can be implemented with the gSOAP tools. Either described by WSDLs or by XML schemas converted to C/C++ source code by wsdl2h, or by using the JSON libraries included with gSOAP to develop JSON applications.

Furthermore, the gSOAP tools can be just as easily used to develop C/C++ applications that efficiently consume and produce XML by leveraging XML data bindings for C/C++ based on XML schemas. Basically, an XML schema has an equivalent set of C/C++ data types for the components described by the schema. So XML schema strings are just C/C++ strings, XML schema enumerations are C/C++ enums, XML schema complex types are just structs and classes in C/C++, and so on. This enhances the reliability and safety of XML applications, because type-safe serializable C/C++ data types are serialized and validated in XML automatically.

This XML data binding means that your XML data is simply represented as C/C++ data. Reading and writing XML is a lot easier than using a DOM or SAX library for XML. This is not more expensive or more complex than it sounds. In fact, the generated XML serializers are very efficient to parse and validate XML and may run more than 30 times faster than validating XML parsers such as Apache Xerces C++.

In summary, gSOAP offers a type-safe and transparent approach to develop XML applications that has proven to be quicker to develop (by auto-coding), safer (by XML validation and type-safety), more reliable (by auto-generation of XML test messages and warnings), and higher performing (by efficient serializers and XML parsers generated in C/C++), compared to DOM and SAX libraries.

This user guide explains the gSOAP tools and libraries. This user guide and additional documentation for the growing number of gSOAP plugins can be found at https://www.genivia.com/doc. A getting-started guide for developers is available at https://www.genivia.com/dev.html with a tutorial on common topics at https://www.genivia.com/tutorials.html. Various examples ranging from simple calculator service APIs to very large protocols spanning dozens of WSDLs can be found at https://www.genivia.com/examples.html. For frequently asked questions see https://www.genivia.com/resources.html for help.

🔝 Back to table of contents

Notational conventions

The typographical conventions used by this document are:

  • Courier denotes C and C++ source code.
  • Courier denotes XML content, JSON content, file and path names, and URIs.
  • Courier denotes HTTP content, text file content, and shell commands with command line options and arguments.

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.

🔝 Back to table of contents

Tooling characteristics

  • Safety: the tools generate type-safe XML serialization functions for native and user-defined C and C++ data structures.
  • Protocols: WSDL 1.1, WSDL 2.0, REST, SOAP 1.1, SOAP 1.2, SOAP RPC encoding style, SOAP document/literal style, SOAP-over-UDP, WS-Security, WS-Addressing, WS-ReliableMessaging, WS-Discovery, WS-Trust, WS-Policy, JSON REST/RPC, XML-RPC, Atom and RSS. JSON is supported as a library bundled with the XML-RPC library to switch between XML-RPC and JSON protocols (since these are similar, speaking data wise). For more details, see the gsoap/samples/xml-rpc-json folder in the gSOAP package and the XML-RPC and JSON documentation.
  • SOAP: implements the full range of SOAP 1.1/1.2 specifications, including RPC encoding and document/literal messaging styles.
  • XSD: supports all XML schema 1.0 and 1.1 schema type constructs and has been tested against the W3C XML Schema Patterns for Databinding Interoperability working group.
  • XML: implements a fast schema-specific XML pull parser that does not require intermediate storage of XML in a DOM to deserialize data.
  • HTTP: HTTP 1.0/1.1, IPv4 and IPv6, HTTPS (requires OpenSSL 3.0 or the latest or GNUTLS or WolfSSL), cookies, authentication, Zlib deflate and gzip compression, and connecting through HTTP proxies.
  • Attachments: MIME (SwA), DIME, and MTOM attachments are supported. Streaming capabilities to direct the data stream to/from resources using user-defined callbacks. gSOAP is the only toolkit that supports streaming MIME, DIME, and MTOM attachment transfers, which allows you to exchange binary data of practically unlimited size in the fastest possible way by streaming, while ensuring the usefulness of XML interoperability.
  • Debugging: compilation of client and service applications in DEBUG mode traces their engine activity for debugging, verifies memory usage (leak detection), and saves message logs for inspection.
  • Testing: soapcpp2 -T generates server source code that automatically implements echo message services for testing. The testmsgr tool generates XML messages from message templates for server-side and client-side black-box testing, see Test Messenger documentation. In addition, the soapcpp2 tool generates sample SOAP/XML input and output messages for verification and testing.
  • Speed: the soapcpp2-generated high-performance XML serializers are ideal for building efficient Web services that are compute-intensive and are therefore best written in C or C++.
  • Portability: source code is portable and compiles on Windows, Unix, Linux, Mac OS X, Symbian, VXWorks, and embedded systems that run WinCE, Symbian, embedded Linux, and so on. The memory footprint of gSOAP client and service applications is small.
  • Footprint: input and output buffering is used to increase efficiency and reduce memory usage. Input and output messages are not fully buffered or stored in a DOM unless required or specified. As a result, large messages can be transmitted by low-memory devices.

🔝 Back to table of contents

API documentation modules

The API documentation is broken down into the following functional documentation modules that drill down into the lower-level API of macros, functions, context and context variables, plugins and more:

Debugging and loggingThis module defines compile-time flags and functions for run-time debugging and logging
WITH_MACRO compile-time flagsThis module defines the WITH_MACRO compile-time flags to configure the engine build
SOAP_MACRO compile-time valuesThis module defines the SOAP_MACRO compile-time values to configure the engine build
SOAP_MACRO run-time flagsThis module defines the SOAP_MACRO run-time soap_mode flags to set the engine mode
SOAP_MACRO run-time error codesThis module defines the SOAP_MACRO run-time soap_status error codes returned by functions and stored in soap::error
Context with engine stateThis module defines the soap context structure with the engine state and functions to allocate, initialize, copy and delete contexts
Callback functionsThis module defines the callback functions of the soap context to modify its behavior, as is done by plugins
SSL/TLS context and functionsThis module defines functions to set the SSL/TLS context for HTTPS and WS-Security
HTTP and IO functionsThis module defines functions for HTTP operations and functions for receiving and sending data
HTTP cookie functionsThis module defines functions to set and get HTTP cookies at the server side
Conversion functionsThis module defines conversion functions of values of various types to and from strings
XML namespace tablesThis module defines the Namespace XML namespace structure and function to activate a table
Header structure and functionsThis module defines the SOAP_ENV__Header structure and soap_header function to allocate the header
Fault structure and functionsThis module defines the SOAP_ENV__Fault structure and functions to set and get fault information
DIME attachment functionsThis module defines functions to set and get DIME attachments
MIME attachment functionsThis module defines functions to set and get MIME/MTOM attachments
Plugins and plugin registry functionsThis module defines plugin registry functions to register plugins
Thread and mutex functionsThis module defines portable thread and mutex functions
Miscellaneous functionsThis module defines other useful functions

🔝 Back to table of contents

Getting started

To start using gSOAP, you will need:

The gSOAP source code package includes:

  • The wsdl2h data binding tool that converts WSDLs and XSDs to generate interface header files for soapcpp2. The source code of the wsdl2h tool is located in gsoap/wsdl.
  • The soapcpp2 code generation tool that takes an interface header file and generates the C/C++ Web service binding implementation source code. The source code of the soapcpp2 tool is located in gsoap/src.
  • The run-time engine gsoap/stdsoap2.h and source code gsoap/stdsoap2.c for C and gsoap/stdsoap2.cpp for C++. These are compiled into the C libraries gsoap/libgsoap.a (without OpenSSL/GNUTLS for SSL/TLS), gsoap/libgsoapssl.a (with OpenSSL/GNUTLS for SSL/TLS and with gsoap/dom.c for DOM API), and the C++ libraries gsoap/libgsoap++.a (without OpenSSL/GNUTLS for SSL/TLS), gsoap/libgsoapssl++.a (with OpenSSL/GNUTLS for SSL/TLS and with gsoap/dom.cpp for DOM API). There are two more versions of these libraries with HTTP cookies enabled.
  • Several examples of gSOAP applications and other development tools that are build with wsdl2h and soapcpp2 are located in gsoap/samples.
  • XML DOM API and the domcpp code generation tool located in gsoap/samples/dom, see also the XML DOM API and domcpp documentation.
  • JSON and XML-RPC libraries and the jsoncpp code generation tool located in gsoap/samples/xml-rpc-json, see also the XML-RPC and JSON documentation.
  • An XML Web API testing tool located in gsoap/samples/testmsgr, see also the Test Messenger documentation.
  • Plugins to enhance the capabilities of the engine and to support WS protocols such as WS-Security, WS-Addressing, WS-ReliableMessaging, and WS-Discovery. The plugins are located in gsoap/plugin and gsoap/mod_gsoap. Most but not all plugins are imported into interface header files for soapcpp2 with the #import directive. See also API documentation Module Plugins and plugin registry functions.
  • Custom serializers for several C and C++ types to enhance the capabilities of XML serialization, located in gsoap/custom. Custom serializers are imported into interface header files for soapcpp2 with the #import directive. This is usually done via a typemap.dat file for wsdl2h that specifies bindings for XML schema types to C/C++ types, including custom serializers when desired.

The wsdl2h and soapcpp2 tools and the gSOAP libraries are build with ./configure and make, see the download and installation page https://www.genivia.com/downloads.html for the most recent versions of gSOAP and gSOAP software installation details. The examples and other tools are build with ./configure --enable-samples and make.

Non-SSL-enabled (that is, not HTTPS capable) versions of the binaries of the wsdl2h and soapcpp2 tools are also included in the gSOAP package in gsoap/bin for Windows and Mac OS platforms. The SSL-enabled and HTTPS-capable wsdl2h tool is only available for download from https://www.genivia.com/downloads.html with a commercial-use license and download key.

Although gSOAP tools are available in binary format for several platforms, the code generated by these tools is equivalent. This means that the generated source code files can be transferred between platforms and locally compiled.

The examples given in this document do not require the libraries of the engine to be build, but rather show the use of the source code: gsoap/stdsoap2.c and gsoap/dom.c (or gsoap/stdsoap2.cpp and gsoap/dom.cpp for C++). Using the source code instead of the libraries gives more control when we use the -DWITH_MACRO and -DSOAP_MACRO compile-time options, see also Modules WITH_MACRO compile-time flags and SOAP_MACRO compile-time values.

🔝 Back to table of contents

Where to find examples

Introductory examples can be found online at https://www.genivia.com/dev.html. Additional examples can be found online at https://www.genivia.com/examples.html.

The gSOAP package also contains numerous examples in the gsoap/samples directory. Run make inside these directories to build the example applications. The examples are meant to demonstrate basic to advanced features of gSOAP. Some examples are actually development tools and libraries, such as Test Messenger located in gsoap/samples/testmsgr to test XML Web APIs, the XML DOM API and domcpp located in gsoap/samples/dom to generate XML DOM API source code from XML files, the JSON API and jsoncpp located in gsoap/samples/xml-rpc-json to generate JSON API source code from JSON files.

Advanced examples include a streaming MTOM attachment server and client application demonstrate high-performance file exchanges, located in gsoap/samples/mtom-stream. An SSL-secure Web server application demonstrates the generation of dynamic content for Web browsing and Web services functionality at the same time, located in gsoap/samples/webservice.

🔝 Back to table of contents

Creating a SOAP/XML client application

This section explains the basics to develop a SOAP/XML client application in C and C++. We refer to https://www.genivia.com/dev.html for additional introductory examples of SOAP/XML, XML REST and JSON applications in C and C++.

The wsdl2h tool imports one or more WSDLs and XML schemas and generates a gSOAP interface file for soapcpp2 with familiar C/C++ header file syntax to define the Web service operations and the C/C++ data types. The soapcpp2 tool then takes this header file and generates XML serializers for the data types (soapStub.h, soapH.h, and soapC.cpp), the client-side stub functions (soapClient.cpp), and server-side skeleton functions (soapServer.cpp).

The soapcpp2 tool can also generate WSDL definitions to implement a service from scratch, i.e. without defining a WSDL first. This "closes the loop" in that it enables Web services development from WSDL or directly from a set of C/C++ operations declared as functions in an interface header file for soapcpp2 without the need for users to be experts in WSDL and XSD.

You only need to follow a few steps to execute the tools from the command line or using a Makefile (see also MSVC++ project examples in the gsoap/samples directory with tool integration in the MSVC++ IDE). For example, to generate code for the calculator Web service, we run the wsdl2h tool from the command line on the URL of the WSDL and use wsdl2h -o calc.h option -o calc.h to specify the calc.h interface file to output:

 wsdl2h -o calc.h http://www.genivia.com/calc.wsdl

This generates the calc.h service definition interface file with service operation definitions and types to pass with the operation parameters. This interface file is then input to the soapcpp2 tool to generate the stub and skeleton source code and XML serialization functions. The calc.h file includes documentation extracted form the WSDL and introductions to process the file with soapcpp2. You can use Doxygen (http://www.doxygen.org) to automatically generate the documentation pages for your development from the generated calc.h interface file. To generate a markdown report for your client, use soapcpp2 -r option -r which has more details than the calc.h file.

In this example we will develop a C++ API for the calculator service. By default, the wsdl2h tool generates C++ with containers and other C++ std data types. To build without C++ containers and other std types, use wsdl2h -s option -s:

 wsdl2h -s -o calc.h http://www.genivia.com/calc.wsdl
Note
Visual Studio IDE users should make sure to compile all gSOAP source code files in C++ compilation mode. If you migrate to a project file .vcproj, please set CompileAs="2" in your .vcproj file.

We have not yet generated the stub functions and serializers for our C++ client application to invoke remote service operations. To do so, we run the soapcpp2 tool as follows:

 soapcpp2 -j -C -Iimport calc.h

Option -j (and alternatively option -i) indicates that we want C++ proxy and server objects that include the client (and server) code, option -C indicates client-side only files are generated (soapcpp2 generates both client stub functions and server skeleton functions by default). Option -I is needed to import the stlvector.h file from the gsoap/import directory located in the gSOAP source code package to support serialization of vectors, when applicable.

We use the generated soapcalcProxy class declared and defined in soapcalcProxy.h and soapcalcProxy.cpp, and calc.nsmap XML namespace mapping table to access the Web service. The soapcalcProxy.h file includes documentation on the proxy class. The soapcalcProxy class is a proxy to invoke the remote service:

// File: calclient.cpp
#include "soapcalcProxy.h"
#include "calc.nsmap"
int main()
{
calcProxy service;
double result;
if (service.add(1.0, 2.0, result) == SOAP_OK)
std::cout << "The sum of 1.0 and 2.0 is " << result << std::endl;
else
service.soap_stream_fault(std::cerr);
service.destroy(); // delete data and release memory
}

To complete the build, compile the code above and compile this with the generated soapC.cpp and soapcalcProxy.cpp files, and link the engine with -lgsoap++:

c++ -o calcclient calcclient.cpp soapcalcProxy.cpp soapC.cpp -lgsoap++

Alternatively, compile gsoap/stdsoap2.cpp:

c++ -o calcclient calcclient.cpp soapcalcProxy.cpp soapC.cpp stdsoap2.cpp

In both cases it is assumed that stdsoap2.h and the soapcpp2-generated soapcalcProxy.h, soapStub.h, soapH.h, and calc.nsmap files are located in the current directory.

Then run the example:

./calcclient
The sum of 1.0 and 2.0 is 3

To build a pure C application, use wsdl2h -c option -c and run soapcpp2 -C to generate the client stub functions and serializers:

 wsdl2h -c -o calc.h http://www.genivia.com/calc.wsdl
 soapcpp2 -C -Iimport calc.h

In this case our code uses a simple C function call to invoke the service and we also need to explicitly delete data and the context with soap_end and soap_free:

// File: calclient.c
#include "soapH.h" // include the generated declarations
#include "calc.nsmap" // include the generated namespace table
int main()
{
struct soap *soap = soap_new();
double result;
if (soap_call_ns__add(soap, 1.0, 2.0, &result) == SOAP_OK)
printf("The sum of 1.0 and 2.0 is %lg\n", result);
else
soap_print_fault(soap, stderr);
soap_destroy(soap); // delete managed objects
soap_end(soap); // delete managed data and temporaries
soap_free(soap); // finalize and delete the context
}

To complete the build, compile the code above and compile this with the generated soapC.c and soapClient.c files, and link the engine with -lgsoap:

cc -o calcclient calclient.c soapClient.c soapC.c -lgsoap

Alternatively, compile gsoap/stdsoap2.c:

cc -o calcclient calclient.c soapClient.c soapC.c stdsoap2.c

In both cases it is assumed that stdsoap2.h and the soapcpp2-generated soapStub.h, soapH.h, and calc.nsmap files are located in the current directory.

Then run the example:

./calcclient
The sum of 1.0 and 2.0 is 3

The calculator example is fairly simple and used here to illustrate the development steps from code generation to running the application. The development steps for large-scale XML applications is similar.

See https://www.genivia.com/dev.html for additional introductory examples of SOAP/XML, XML REST and JSON applications in C and C++. See https://www.genivia.com/examples and the examples located in the gSOAP source code package in the gsoap/samples directory.

🔝 Back to table of contents

Creating a SOAP/XML service application

This section explains the basics to develop a SOAP/XML service application in C and C++. We refer to https://www.genivia.com/dev.html for additional introductory examples of SOAP/XML, XML REST and JSON applications in C and C++.

Developing a service application is easy. In this example we will demonstrate a service deployed with the Common Gateway Interface (CGI) because it is a simple mechanism. This is not the preferred deployment mechanism. Because CGI is slow and stateless. FastCGI improves the speed of CGI applications, but is still stateless. Faster services can be developed as a stand-alone gSOAP HTTP/HTTPS servers as explained in this section further below.

To deploy services in a public and possibly hostile environment, we recommend the use of Apache module or IIS ISAPI extension to manage and protect services. An Apache module plugin and ISAPI extension plugin is included in the gSOAP package under gsoap/mod_gsoap. For details, see:

To deploy gSOAP stand-alone services in a public environment make sure to protect your service application as explained in Sections Safety guards and Timeout management for non-blocking operations. See also our tutorials https://www.genivia.com/tutorials.html with instructions to protect your online gSOAP Web APIs.

Let's get started. Suppose we want to implement a simple CGI-based service that returns the time in GMT. For this example we start with an interface header file for soapcpp2, currentTime.h which contains the service definitions. Such a file can be obtained from a WSDL using wsdl2h when a WSDL is available. When a WSDL is not available, you can define the service in C/C++ definitions in a newly created interface header file and let the soapcpp2 tool generate the source code and WSDL for you.

The currenTime service operation of our Web service has only one output parameter, which is the current time defined in our currentTime.h service specification:

// File: currentTime.h
//gsoap ns service name: currentTime
//gsoap ns service namespace: urn:currentTime
//gsoap ns service location: http://www.yourdomain.com/currentTime.cgi
int ns__currentTime(time_t& response);

Note that we associate an XML namespace prefix ns and namespace name urn:currentTime with the service WSDL and SOAP/XML messages. The gSOAP tools use a special convention for identifier names that are part of a namespace: a namespace prefix (ns in this case) followed by a double underscore __. This convention is used to resolve namespaces and to avoid name clashes. The ns namespace prefix is bound to the urn:currentTime namespace name with the //gsoap directive. The //gsoap directives are used to set the properties of the service, in this case the name, namespace, and location endpoint.

The service implementation for CGI simply requires a soap_serve call on a soap context created with soap_new. The service operations are implemented as functions, which are called by the service skeleton functions via the service request dispatcher soap_serve:

// File: currentTime.cpp
#include "soapH.h" // include the generated declarations
#include "currentTime.nsmap" // include the generated namespace table
int main()
{
// create soap context and serve one CGI-based request:
struct soap *soap = soap_new1(SOAP_XML_INDENT);
soap_serve(soap);
soap_destroy(soap); // delete managed class instances
soap_end(soap); // delete managed data and temporaries
soap_free(soap); // finalize and free the context
}
int ns__currentTime(struct soap *soap, time_t& response)
{
response = time(0);
return SOAP_OK;
}

Note that we pass the soap context with the engine context information to the service operation function as the first argument. The soap context comes in handy to determine properties of the connection and to dynamically allocate data with soap_malloc or with the generated soap_new_T functions for serializable types T that will be automatically deleted by calling soap_destroy and soap_end when the service operation is done and the service loop rolls over.

We run the soapcpp2 tool on the header file to generate the server-side code:

 soapcpp2 -S currentTime.h

and then compile the CGI binary:

 c++ -o currentTime.cgi currentTime.cpp soapServer.cpp soapC.cpp stdsoap2.cpp

To activate the service, copy the currentTime.cgi binary to your bin-cgi directory and set the proper file permissions.

The soapcpp2 tool generated the WSDL definitions currentTime.wsdl. You can use the WSDL to advertise your service. You don't need to use this WSDL to develop a gSOAP client. You can use the currentTime.h file with soapcpp2 -C option -C to generate client-side code.

Since CGI is very simple, a convenient aspect of CGI is that it exchanges messages over standard input and output. Therefore, you can run the CGI binary on the auto-generated example request XML file currentTime.currentTime.req.xml to test your server:

 ./currentTime.cgi < currentTime.currentTime.req.xml

and this displays the HTTP server response:

Status: 200 OK
Server: gSOAP/2.8
X-Frame-Options: SAMEORIGIN
Content-Type: text/xml; charset=utf-8
Content-Length: 460
Connection: close
1 <?xml version="1.0" encoding="UTF-8"?>
2 <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="urn:currentTime">
3  <SOAP-ENV:Body>
4  <ns:currentTimeResponse>
5  <response>2018-10-25T17:17:03Z</response>
6  </ns:currentTimeResponse>
7  </SOAP-ENV:Body>
8 </SOAP-ENV:Envelope>

The above approach works also for C. Just use soapcpp2 -c -S option -c in addition to the -S option to generate C code. Of course, in C we use pointers instead of references and the currentTime.h file should be adjusted to use C syntax and types.

Run soapcpp2 -r -c -S option -r to generate a soapReadme.md report. This report includes many details about the service operations and serializable C/C++ data types declared in the interface header file. This markdown file can be converted to HTML or other document formats with tools such as Doxygen.

A more elegant server implementation in C++ can be obtained by using soapcpp2 -j -S option -j (or option -i) to generate C++ client-side proxy and server-side service objects as classes that you can use to build clients and services in C++. The option removes the generation of soapClient.cpp and soapServer.cpp, since these are no longer needed when we have classes that implement the client and server logic:

 soapcpp2 -j -S currentTime.h

This generates soapcurrentTimeService.h and soapcurrentTimeService.cpp files, as well as auxiliary files soapStub.h, soapH.h, and soapC.cpp and currentTime.nsmap. The soapcurrentTimeService.h file includes documentation on the service class.

Now using the currentTimeService class we can improve the CGI service application:

// File: currentTime.cpp
#include "soapcurrentTimeService.h" // include the proxy declarations
#include "currentTime.nsmap" // include the generated namespace table
int main()
{
// create server and serve one CGI-based request:
currentTimeService server(SOAP_XML_INDENT);
server.serve();
server.destroy();
}
int currentTimeService::currentTime(time_t& response)
{
response = time(0);
return SOAP_OK;
}

We compile this with:

 c++ -o currentTime.cgi currentTime.cpp soapcurrentTimeService.cpp soapC.cpp stdsoap2.cpp

and install the binary as a CGI application.

To run the server as a stand-alone iterative (i.e. non-multi-hreaded) server on port 8080 until a the accept times out or an error occurs:

if (server.run(8080) != SOAP_OK)
server.soap_stream_fault(std::cerr);

To run the server as a stand-alone iterative server on port 8080 while ignoring common errors until a TCP occurs:

while (server.run(8080) != SOAP_OK)
{
if (server.soap->error == SOAP_TCP_ERROR)
break;
server.soap_stream_fault(std::cerr);
}

To implement stand-alone and multi-threaded services, see Sections How to create a stand-alone server and How to create a multi-threaded stand-alone service. These stand-alone Web Services handle multiple SOAP requests by spawning a thread for each request. Thread pooling is also an option. The use of Apache modules and ISAPI extensions to deploy gSOAP services is recommended to ensure load balancing, access control, tracing, and so on.

For more information on server-side service classes, see Section How to generate C++ server classes . For more information on client-side proxy classes, see Section How to generate C++ client proxy classes .

See https://www.genivia.com/dev.html for additional introductory examples of SOAP/XML, XML REST and JSON applications in C and C++. See https://www.genivia.com/examples and the gSOAP source code package gsoap/samples for more examples.

🔝 Back to table of contents

Introduction to XML data bindings

This section is a basic introduction to XML data bindings in C/C++. Because gSOAP offers many options to implement XML data bindings, we wrote a separate C and C++ XML data bindings document on this topic that contains an in-depth discussion of XML schema mappings to C/C++ types, using wsdl2h with typemap.dat to customize these bindings, memory management to allocate and release serializable types, and how to use soapcpp2 options to generate deep data structure copy and delete functions for serializable types.

Basically, the C/C++ XML data binding in gSOAP provides and automated mechanism to serialize any C and C++ data structure in XML and to deserialize XML back into C/C++ data structures. To facilitate XML data bindings, a WSDL or an XML schema (XSD file) can converted with wsdl2h into a set of serializable C or C++ data type declarations. These C/C++ type declarations can be readily incorporated into your application to manipulate XML directly as C/C++ data structures with much more ease than DOM or SAX. For example, XML schema strings are just C/C++ strings, XML schema enumerations are C/C++ enums, XML schema complex types are just structs or classes in C/C++, and so on. In this way, an automatic mapping between XML elements of the XML schema and members of a class is created. No DOM traversals and SAX events are needed.

More importantly, the XML C/C++ data binding makes XML manipulation type safe. That is, the type safety of C/C++ ensures that only valid XML documents can be parsed and generated.

The wsdl2h tool performs the mapping of WSDL and XML schemas to C and/or C++ automatically. The output of wsdl2h is a "data binding interface file" which is simply an annotated C/C++ header file with the serializable C/C++ data types that represent XML schema components. This file also includes comments and documentation of the serializable data types. For WSDLs, also functions are declared in this interface file that represent XML Web services operations.

Let's illustrate XML data bindings with an example. Suppose we have an XML document with a book record:

1 <book isbn="1234567890">
2  <title>Farewell John Doe</title>
3  <publisher>ABC's is our Name</publisher>
4 </book>

An example XML schema (XSD file) that defines the book element and type could be:

1 <schema ...>
2  <element name="book">
3  <complexType>
4  <sequence>
5  <element name="title" type="string" minOccurs="1"/>
6  <element name="publisher" type="string" minOccurs="1"/>
7  </sequence>
8  <attribute name="isbn" type="unsignedLong" use="required"/>
9  </complexType>
10  </element>
11 </schema>

Now, using wsdl2h we translate this XML schema that defines the book type and root element into a C++ class definition:

class book
{ public:
@ ULONG64 isbn;
std::string title;
std::string publisher;
};

Note that annotations such as @ are used to distinguish attributes from elements. These annotations are gSOAP-specific and are handled by the soapcpp2 tool that reads this generated interface file and generates the XML data binding implementation with serializers for the data types declared in the interface file.

That is, the soapcpp2 tool generates all the necessary code to parse and generate XML for book objects. Validation constraints such as minOccurs="1" and use="required" are included in the generated code as checks that are enforced with the SOAP_XML_STRICT flag.

To write the XML representation of a book object instantiated in our C++ application, we first create a soap engine context and use it with soap_write_book (a function generated by soapcpp2) to write the object in XML to standard output:

struct soap *soap = soap_new1(SOAP_XML_INDENT); // new context
book bk;
bk.isbn = 1234567890;
bk.title = "Farewell John Doe";
bk.publisher = "ABC's is our Name";
if (soap_write_book(soap, &bk) != SOAP_OK)
... // error
soap_destroy(soap); // delete managed class instances
soap_end(soap); // delete managed data and temporaries
soap_free(soap); // finalize and free the context

The soap context holds the engine state and run-time flags, such as SOAP_XML_INDENT, serialization options, and other I/O settings. This means that we can simply set the output file descriptor int soap::sendfd or output stream std::ostream* soap::os of the context to redirect the content to a file or string. Also, when serializing a graph with SOAP_XML_GRAPH rather than an XML tree, the XML output contains id-ref attributes to reference nodes in the XML graph, similar to the way SOAP encoding with multi-reference id-ref/href works, see Section How to use XML serializers to save and load application data for details.

To read the XML representation from standard input into a book class instance:

struct soap *soap = soap_new1(SOAP_XML_STRICT); // new context
book bk;
if (soap_read_book(soap, &bk) != SOAP_OK)
... // error
else
cout << bk.isbn << ", " << bk.title << ", " << bk.publisher << endl;
... // further use of bk
soap_destroy(soap); // delete managed class instances
soap_end(soap); // delete managed data and temporaries
soap_free(soap); // finalize and free the context

Automatic built-in strict XML validation is enabled with SOAP_XML_STRICT, which ensures that data members are present so we can safely print them in this example, thus ensuring consistency of data with the XML schema.

We can set the int soap::recvfd file descriptor or the std::istream* soap::is input stream to read from a file or string stream instead of stdin.

The soap_destroy and soap_end calls deallocate the deserialized data, so use these with care. Memory management is automatic in gSOAP to avoid leaks.

The above example uses a very simple example schema. The gSOAP toolkit handles all XML schema constructs defined by the XML schema standard. The toolkit is also able to serialize pointer-based C/C++ data structures, including cyclic graphs, structs/classes, unions, enums, containers, and even special data types such as struct tm. Therefore, the toolkit works in two directions: from WSDL/schema to C/C++ and from C/C++ to WSDL/schema.

The gSOAP toolkit also handles multiple schemas defined in multiple namespaces. Normally the namespace prefixes of XML namespaces are added to the C/C++ type definitions to ensure type uniqueness. For example, if we would combine two schemas in the same application where both schemas define a book object, we need to resolve this conflict. In gSOAP this is done using namespace prefixes, rather than C++ namespaces (research has pointed out that XML namespaces are not equivalent to C++ namespaces). Thus, the book class might actually be bound to an XML namespace and the class would be named ns__book, where ns is bound to the corresponding namespace.

For example, the following run-time flags are available to control serialization as an XML tree or graph:

struct soap *soap = soap_new();
soap_set_mode(soap, SOAP_XML_TREE); // use this for XML without id-ref (no cycles!)
soap_set_mode(soap, SOAP_XML_GRAPH); // or use this for XML with id-ref (including cycles)

Other flags can be used to format XML, see Section Run-time flags .

For more details on XML databinding support for C and C++, see Section How to use XML serializers to save and load application data and the C and C++ XML data bindings document.

🔝 Back to table of contents

A quick user guide

This section of the user guide presents a quick overview to get started with gSOAP. In principle, XML SOAP and REST clients and services can be developed in C and C++ with the soapcpp2 tool without a detailed understanding of XML, XML schema, WSDL, and the XML SOAP protocol.

🔝 Back to table of contents

How to build Web API clients

The implementation of a client application that invokes remote service operations by serializing application data in XML for the remote operation request, also called XML marshalling in remote procedure calling. This requires a "stub function", also called "service proxy", for each service operation that the client invokes. The primary stub's responsibility is to serialize the parameter data in XML, send the request with the parameters to the designated SOAP service over the wire, to wait for the response, and to deserialize the parameter data of the response when it arrives. With the stub function in place, the client application invokes a remote service operation as if it would invoke a local function. To write a client stub function in C or C++ by hand is a tedious task, especially if the input and output parameters of a service operation contain elaborate data structures that must meet XML validation constraints. Fortunately, the wsdl2h tool and soapcpp2 tool automate the development of SOAP/XML Web service client and server applications.

The soapcpp2 tool generates the necessary gluing code (the client stub functions and server skeleton functions) to build web service clients and services. The input to the soapcpp2 tool consists of an interface file with familiar C/C++ header file syntax. This interface header file can be automatically generated from a WSDL (Web Service Description Language) documentation of a service with the gSOAP wsdl2h tool.

The following command:

 wsdl2h -o calc.h http://www.genivia.com/calc.wsdl

generates the calc.h interface header file for soapcpp2. The WSDL specification may consist of multiple imported WSDL files and XSD schema files. The WSDLs and XSDs are then translated to C or C++, replacing WSDL service operation by C/C++ functions and XML schema data types by C/C++ data types.

To generate C code, we use wsdl2h -c option -c:

 wsdl2h -c -o calc.h http://www.genivia.com/calc.wsdl

For more details on the wsdl2h tool and its options, see Section The wsdl2h tool .

When upgrading gSOAP to a newer version it is often not necessary to perform this first step again, since newer versions are backward compatible to previous interface header files generated by wsdl2h.

Looking into the file calc.h we see that the SOAP service methods are specified as function prototypes. For example, the add function to add two doubles is declared as:

int ns2__add(double a, double b, double& result);

The ns2__add function uses an XML namespace prefix to distinguish it from operations defined in other namespaces, thus nicely preventing name clashes in this way. The convention to add an XML namespace prefix to the names of operations, types, and struct and class members is universally used by the gSOAP tools and automatically added by wsdl2h.

Next, the calc.h header file is input to the soapcpp2 tool to generate the gluing code's logic to invoke the Web service from a client application:

soapcpp2 calc.h

The function prototypes in calc.h are converted by the soapcpp2 tool to stub functions for remote calls:

  • soapStub.h annotated copy of the header file's definitions.
  • soapH.h XML serializers declarations
  • soapC.cpp XML serializers implementations
  • soapClient.cpp the client calling stub functions

The logic of the generated soapClient.cpp stub functions allow client applications to seamlessly interact with existing SOAP Web services as illustrated by the client code example in the next section.

The input and output parameters of a service operation may be primitive data types or compound data types, such as containers and pointer-based linked data structures. These are defined in the interface header file, which is either generated by the wsdl2h tool or it may be specified by hand. The soapcpp2 tool automatically generates XML serializers and XML deserializers for these data types to enable the generated stub functions to serialize the contents of the parameters of the service operations in XML.

The soapcpp2 tool also generates "skeleton functions" soapServer.cpp for each of the service operations specified in the interface header file. The skeleton functions can be readily used to implement one or more of the service operations in a new XML Web service.

To develop C++ client applications, a useful option to use with soapcpp2 is -j to generate proxy classes to invoke services, instead of global functions:

soapcpp2 -j calc.h

The function prototypes in calc.h are converted by the soapcpp2 tool to the following function:

  • soapStub.h annotated copy of the header file's definitions.
  • soapH.h XML serializers declarations
  • soapC.cpp XML serializers implementations
  • soapcalcProxy.h the client proxy class
  • soapcalcProxy.cpp the client proxy class implementation

To use the proxy class, #include "soapcalcProxy.h" and compile and link soapcalcProxy.cpp. See the following section.

🔝 Back to table of contents

Example

The add service operation declared in the calc.h file obtained with the wsdl2h tool in the previous section, adds two doubles. The WSDL description of the service provides the endpoint to invoke the service operations and the XML namespace used by the operations:

Each SOAP-specific service operation also has a "SOAP action", which is an optional string to identify the operation, which is mainly used with WS-Addressing. The request and response messages for SOAP RPC-encoded services are simply represented by C functions with input and output parameters. For the add operation, the SOAP binding details are:

  • SOAP style: RPC
  • SOAP encoding: encoded
  • SOAP action: ""

This information is translated to the wsdl2h-generated interface header file with the service definitions. The calc.h header file for C++ generated by wsdl2h contains the following directives and declarations:

//gsoap ns2 schema namespace: urn:calc
//gsoap ns2 schema form: unqualified
//gsoap ns2 service name: calc
//gsoap ns2 service type: calcPortType
//gsoap ns2 service port: http://example.com/service.cgi
//gsoap ns2 service namespace: urn:calc
//gsoap ns2 service transport: http://schemas.xmlsoap.org/soap/http
//gsoap ns2 service method-protocol: add SOAP
//gsoap ns2 service method-style: add rpc
//gsoap ns2 service method-encoding: add http://schemas.xmlsoap.org/soap/encoding/
//gsoap ns2 service method-action: add ""
int ns2__add(double a, double b, double& result);

The actual contents may vary depending on the release version and the options used to control the output.

The other calculator service operations are similar and were elided for clarity.

The //gsoap directives are interpreted by the soapcpp2 tool to generate code that is compliant to the SOAP protocol. For this service the SOAP protocol with the "SOAP 1.1 RPC encoding style" is used. This produces XML messages with the familiar SOAP envelope and body in the SOAP 1.1 namespace and a SOAP-ENV:encodingStyle attribute for SOAP RPC encoding (a simple XML serialization format) as can be seen in the XML request message:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <SOAP-ENV:Envelope
3  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
4  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
5  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
7  xmlns:ns="http://tempuri.org/ns.xsd">
8  <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/>
9  <ns:add>
10  <a>1.0</a>
11  <b>2.0</b>
12  </ns:add>
13  </SOAP-ENV:Body>
14 </SOAP-ENV:Envelope>

Another style is "document/literal" which is also defined by SOAP 1.1/1.2.

To use SOAP document/literal style is indicated for each service operation as follows in the interface file specification, which also switches to the SOAP 1.2 protocol:

//gsoap ns2 service method-protocol: add SOAP1.2
//gsoap ns2 service method-style: add document
//gsoap ns2 service method-encoding: add literal
//gsoap ns2 service method-action: add ""
int ns2__add(double a, double b, double& result);

This produces XML messages with the familiar SOAP envelope and body with the SOAP 1.2 namespace and without the encodingStyle attribute as can be seen in the XML request message:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <SOAP-ENV:Envelope
3  xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
4  xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding"
5  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
7  xmlns:ns="urn:calc">
8  <SOAP-ENV:Body>
9  <ns:add>
10  <a>1.0</a>
11  <b>2.0</b>
12  </ns:add>
13  </SOAP-ENV:Body>
14 </SOAP-ENV:Envelope>

REST instead of SOAP is specified with HTTP instead of SOAP with the //gsoap <prefix> service method-protocol: directive to carry our XML messages using the HTTP POST method without a SOAP envelope:

//gsoap ns2 service method-protocol: add HTTP
int ns2__add(double a, double b, double& result);

This produces non-SOAP XML messages with HTTP POST. For example:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <ns:add
3  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
5  xmlns:ns="urn:calc">
6  <a>1.0</a>
7  <b>2.0</b>
8 </ns:add>

Likewise you can specify POST, PUT, and GET HTTP methods for direct XML messaging instead of SOAP. However, all XML Web services protocols such as WS-Security and WS-Addressing require SOAP to include the SOAP Headers.

Note that the function declaration itself and the client-side calling stub and server-side skeleton functions are unchanged. The internals of the generated functions are changed to accommodate the protocol specified with the directives.

For more details about the //gsoap directives, see Section Directives .

So as you can see, the Web service operations are declared as function prototypes. When the parameters of the function are structs and classes, then all of these interdependent data types are included in the wsdl2h-generated header file.

In this simple example the parameters are primitive types. The calculator add operation takes two double floats a and b, and returns the sum in result. By convention, all parameters are input parameters except the last parameter which is an output parameter or specified as void for one-way messages. The last parameter is always a single output parameter, if not void. A struct or class is used to wrap multiple output parameters, see also Section How to specify multiple output parameters . This last parameter must be a pointer or reference. By contrast, the input parameters support pass by value or by pointer, but not pass by C++ reference due to complications when generating compilable source code for the stub and skeleton functions.

The function prototype associated with a service operation always returns an int. The return value indicates success with SOAP_OK (zero) or failure with a nonzero value. See Section Run-time error codes for the error codes.

The role of the namespace prefix (ns2__) in the service operation name specified as a function prototype associates an XML namespace with the service operation as a qualified name, i.e. qualified by means of a namespace prefix. This is discussed in detail in Section XML namespace considerations . Basically, the namespace prefix is added to a function name or type name with a pair of underscores, as in ns2__add, where ns2 is the namespace prefix and add is the service operation name. This mechanism ensures uniqueness of operations and types associated with a service.

When using wsdl2h it is strongly recommended to set the namespace prefix to a name of your choice by modifying the typemap.dat file that is used by wsdl2h. This file can be copied from gsoap/typemap.dat and modified in the local directory where you run wsdl2h. This avoids problems when running wsdl2h on multiple WSDLs where the sequence of prefixes ns1, ns2, and so on are arbitrarily assigned to the services. To choose a prefix name for all the operations and types of a service, say prefix c__ for the calculator service, add the following line to typemap.dat:

c = "urn:calc"

and rerun wsdl2h. Moreover, the typemap.dat configures wsdl2h to use specific bindings and data types for services. The result is that c__add is used to uniquely identify the operation rather than the more arbitrary name ns2__add.

A note on the use of underscores in names: 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 C/C++ identifier name to XML tag name translation . Double underscores separate the namespace prefix from the unqualified part of the qualified name.

Next, the soapcpp2 tool is invoked from the command line to process the calc.h service definitions:

 soapcpp2 calc.h

The tool generates the client stub functions for the service operations. Stub functions can be invoked by a client program to invoke the remote service operations. The interface of the generated stub function is identical to the function prototype in the calc.h service definition file, but with additional parameters to pass the engine's context soap, an endpoint URL (or NULL for the default), and a SOAP action (or NULL for the default):

int soap_call_c__add(struct soap *soap, char *URL, char *action, double a, double b, double& result);

This stub function is saved in soapClient.cpp. The file soapC.cpp contains the serializer and deserializer functions for the data types used by the stub. You can use wsdl2h -c option -c to generate pure C code. Likewise, soapcpp2 -c option -c generates pure C code, if the input interface file is written in C of course.

The soap parameter of the stub function shown above must be a valid pointer to a soap context. The URL parameter when non-NULL overrides the default endpoint address defined by the WSDL and the //gsoap <prefix> service port: directive. The action parameter when non-NULL overrides the default SOAP action defined by the WSDL and the //gsoap <prefix> service method-action: directive.

The following example C/C++ client program uses the generated stub function to invoke the remote service operation:

#include "soapH.h" // include the generated declarations
#include "calc.nsmap" // include the generated namespace table
int main()
{
double sum;
struct soap *soap = soap_new(); // the context
if (soap_call_c__add(soap, NULL, NULL, 1.0, 2.0, &sum) == SOAP_OK)
std::cout << "Sum = " << sum << std::endl;
else // an error occurred
soap_print_fault(soap, stderr); // display the SOAP fault message on the stderr stream
soap_destroy(soap); // delete managed class instances
soap_end(soap); // delete managed data and temporaries
soap_free(soap); // finalize and free the context
return 0;
}

The soap_call_c__add call returns SOAP_OK (zero) on success and a nonzero error on failure. When an error occurred you can print the fault message with the soap_print_fault(struct soap*, FILE*) function. Use soap_sprint_fault(struct soap*, char *buf, size_t len) to save the fault message to a string buffer buf[0...len-1]. Use soap_stream_fault(struct soap*, std::ostream&) to send the fault message to a stream.

The following functions are used to explicitly set up a soap context and finalize it:

A soap context can be reused as many times as necessary and does not need to be reinitialized when doing so. However, a new context is required for each thread that runs independently to guarantee exclusive access to a soap context by each thread.

Also the use of any client calls within an active service operation implemented at the server side requires a new context, since soap_serve is still processing with the current soap context that must be maintained while the service operation and response has not been completed yet.

The soapcpp2 code generator tool generates a service proxy class for C++ client applications (and service objects for server applications) with the soapcpp2 -j option -j (or -i option):

 soapcpp2 -j calc.h

The proxy is defined in:

  • soapcalcProxy.h client proxy class
  • soapcalcProxy.cpp client proxy class

Without the -j option, C-like soapClient.cpp and soapServer.cpp source codes are is generated. Use option -i as an alternative to -j to generate classes with the same functionality, but that are inherited from the soap struct. With the -j option, the soap engine context is a pointer member of the generated proxy and service classes and can therefore be shared with other proxy or service class instances. The choice of option to use is application-dependent, but the choice is also important when services are chained to serve requests on the same server port, see Section How to chain C++ server classes to accept messages on the same port .

The generated C++ proxy class initializes the context and offers the service interface as a collection of methods:

#include "soapcalcProxy.h" // get proxy
#include "calc.nsmap" // include the generated namespace table
int main()
{
calcProxy calc(SOAP_XML_INDENT);
double sum;
if (calc.add(1.0, 2.0, sum) == SOAP_OK)
std::cout << "Sum = " << sum << std::endl;
else
calc.soap_stream_fault(std::cerr);
calc.destroy();
return calc.soap->error; // nonzero when error
}

The proxy constructor takes context mode parameters to initialize the context, e.g. SOAP_XML_INDENT in this example.

The code is compiled and linked with soapcalcProxy.cpp, soapC.cpp, and gsoap/stdsoap2.cpp.

The proxy class name is extracted from the WSDL content and may not always be in a short format. You can change this directive to customize the service name:

//gsoap c service name: calc

then rerun soapcpp2 to generate code that uses the new name.

When the example client application is invoked, a SOAP request is performed:

POST /~engelen/calcserver.cgi HTTP/1.1 
Host: websrv.cs.fsu.edu 
User-Agent: gSOAP/2.8 
Content-Type: text/xml; charset=utf-8 
Content-Length: 464 
Connection: close 
SOAPAction: "" 
1 <?xml version="1.0" encoding="UTF-8"?>
2 <SOAP-ENV:Envelope
3  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
4  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
5  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
7  xmlns:c="urn:calc">
8  <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
9  <c:add>
10  <a>1</a>
11  <b>2</b>
12  </c:add>
13  </SOAP-ENV:Body>
14 </SOAP-ENV:Envelope>

The SOAP response message is:

HTTP/1.1 200 OK 
Date: Wed, 05 May 2010 16:02:21 GMT 
Server: Apache/2.0.52 (Scientific Linux) 
Content-Length: 463 
Connection: close 
Content-Type: text/xml; charset=utf-8 
1 <?xml version="1.0" encoding="UTF-8"?>
2 <SOAP-ENV:Envelope
3  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
4  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
5  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
7  xmlns:ns="urn:calc">
8  <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
9  <ns:addResponse>
10  <result>3</result>
11  </ns:addResponse>
12  </SOAP-ENV:Body>
13 </SOAP-ENV:Envelope>

A client can invoke a sequence of service operations, like so:

#include "soapcalcProxy.h" // get proxy
#include "calc.nsmap" // include the generated namespace table
int main()
{
calcProxy calc(SOAP_IO_KEEPALIVE); // keep-alive improves connection performance
double sum = 0.0;
double val[] = { 5.0, 3.5, 7.1, 1.2 };
for (int i = 0; i < 4; i++)
if (calc.add(sum, val[i], sum))
return calc.soap->error;
std::cout << "Sum = " << sum << std::endl;
calc.destroy();
return 0;
}

In the example shown above, no deserialized data is deallocated until calc.destroy(). To deallocate deserialized data between the calls we change the loop as follows:

for (int i = 0; i < 4; i++)
{
if (calc.add(sum, val[i], sum))
return calc.soap->error;
calc.destroy();
}

Deallocation is safe here, since the float values were copied and saved in sum. In other scenarios we should make sure data is copied to local data structures when the data should be preserved. There are tooling options for deep copy and delete of entire data structures to simplify this task, see Section Generating deep copy and deletion functions.

To delegate deletion to another context for later removal, use soap_delegate_deletion(struct soap *soap_from, struct soap *soap_to). For example:

struct soap soap;
soap_init(&soap);
{
// create proxy
calcProxy calc;
... // data produced, e.g. deserialized data with client-side calls
soap_delegate_deletion(&calc, &soap);
calc.destroy();
}
... // data can still be used
soap_destroy(&soap); // delete managed class instances
soap_end(&soap); // delete managed data and temporaries
soap_done(&soap); // finalize the context

In C we use wsdl2h -c option -c to generate C. The example client calculator program would be written as:

#include "soapH.h"
#include "calc.nsmap"
int main()
{
struct soap soap;
double sum = 0.0;
double val[] = { 5.0, 3.5, 7.1, 1.2 };
int i;
for (i = 0; i < 4; i++)
if (soap_call_c__add(&soap, NULL, NULL, sum, val[i], &sum))
return soap.error;
printf("Sum = %lg\n", sum);
soap_end(&soap);
soap_done(&soap);
return 0;
}

The code above is compiled and linked with the soapcpp2-generated soapClient.c and soapC.c files, and gsoap/stdsoap2.c (or compile with -bgsoap).

🔝 Back to table of contents

XML namespace considerations

The declaration of the ns2__add function prototype discussed in the previous section uses the namespace prefix ns2__ of the service operation XML namespace, which is distinguished by a pair of underscores in the function name to separate the namespace prefix from the service operation name. The purpose of a namespace prefix is to associate a service operation name with a service in order to prevent naming conflicts, e.g. to distinguish identical service operation names used by different services.

Note that the XML response of the service example uses a namespace prefix that may be different (e.g. ns) as long as it bound to the same namespace name urn:calc through the xmlns:ns="urn:calc" 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 function 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 soapcpp2 tool. Service details including namespace bindings may be provided with gSOAP directives in a header file, see Section Directives .

The namespace mapping table is:

struct Namespace namespaces[] =
{
// { "prefix", "URI", "URI-pattern" (optional) }
{ "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"}, // must be fourth (2001 XML Schema)
{ "ns2", "urn:calc"}, // given by the service description
{ NULL, NULL} // end of table
};

The first four namespace entries in the table consist of the standard namespaces used by the SOAP protocol. In fact, the namespace mapping table is explicitly declared to enable a programmer to specify the SOAP encoding style and to allow the inclusion of namespace-prefix with namespace-name bindings to comply to the namespace requirements of a specific SOAP service. For example, the namespace prefix ns2, which is bound to urn:calc by the namespace mapping table shown above, is used by the generated stub function to encode the add request. This is performed automatically by the soapcpp2 tool by using the ns2 prefix of the ns2__add method name specified in the calc.h header file. In general, if a function name of a service operation, struct name, class name, enum name, or member name of a struct or class has a pair of underscores, the name has a namespace prefix that must be defined in the namespace mapping table.

The namespace mapping table will be output as part of the SOAP Envelope by the stub function. For example:

1 <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
2  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
3  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
5  xmlns:ns2="urn:calc">
6 ...

The namespace bindings will be used by a SOAP service to validate the SOAP request.

🔝 Back to table of contents

Example

The incorporation of namespace prefixes into C++ identifier names is necessary to distinguish service operations that share the same name but are provided by separate Web services and/or organizations. It avoids potential name clashes, while sticking to the C syntax since C has no support for namespaces. The C++ proxy classes generated with soapcpp2 -j option -j (or option -i) drop the namespace prefix from the method names.

The namespace prefix convention is also be applied to non-primitive types. For example, class names are prefixed to avoid name clashes when the same name is used by multiple XML schemas. This ensures that the XML databinding never suffers from conflicting schema content. For example:

class e__Address // an electronic address from schema 'e'
{ public:
char *email;
char *url;
};
class s__Address // a street address from schema 's'
{ public:
char *street;
int number;
char *city;
};

At this point you may ask why the gSOAP tools do no use C++ namespaces to implement XML namespaces. The answer is not too complicated. XML namespaces are semantically more rich than C++ namespaces. For example, XML schema complexTypes may reference elements in another schema and these elements may be qualified in XML. There could also be element name clashes when element names are the same but referenced in different schemas. In gSOAP this is resolved by naming struct and class members with the namespace prefix notation, thereby ensuring that name clashes among members cannot occur.

An instance of e__Address is encoded by the generated serializer for this type as an Address element with namespace prefix e:

1 <e:Address>
2  <email>me@home</email>
3  <url>www.me.com</url>
4 </e:Address>

While an instance of s__Address is encoded by the generated serializer for this type as an Address element with namespace prefix s:

1 <s:Address>
2  <street>Technology Drive</street>
3  <number>5</number>
4  <city>Softcity</city>
5 </s:Address>

The namespace mapping table of the client program must have entries for e and s that refer to the XML Schemas of the data types:

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" },
{ "e", "http://www.me.com/schemas/electronic-address" },
{ "s", "http://www.me.com/schemas/street-address" },
{ NULL, NULL }
};

This table is automatically generated by soapcpp2 and saved as a .nsmap file that can be included in the source code.

🔝 Back to table of contents

How to generate C++ client proxy classes

Proxy classes for C++ client applications are automatically generated by the soapcpp2 tool, as was shown in Section Example .

A new and improved code generation capability is implemented in soapcpp2 for C++ proxy classes by using soapcpp2 -j option -j (or option -i). These new proxy classes have a cleaner interface and offer more capabilities compared to the gSOAP 2.7 proxy and service classes.

In C++ you can also use wsdl2h -q name option -q name to generate the proxy class and serializers in the specified C++ namespace name. This is very useful if you want to create multiple proxies for services by repeated use of wsdl2h and then combine them in one code. Alternatively, you can run wsdl2h just once on all service WSDLs and have soapcpp2 generate multiple proxies for you. The latter approach does not use C++ namespaces and actually reduces the overall amount of source code by avoiding code duplication.

To illustrate the generation of a proxy class, the calc.h header file example of the previous section used.

// Content of file "calc.h":
//gsoap ns2 schema namespace: urn:calc
//gsoap ns2 schema form: unqualified
//gsoap ns2 service name: calc
//gsoap ns2 service type: calcPortType
//gsoap ns2 service port: http://websrv.cs.fsu.edu/~engelen/calcserver.cgi
//gsoap ns2 service namespace: urn:calc
//gsoap ns2 service transport: http://schemas.xmlsoap.org/soap/http
//gsoap ns2 service method-protocol: add SOAP
//gsoap ns2 service method-style: add rpc
//gsoap ns2 service method-encoding: add http://schemas.xmlsoap.org/soap/encoding/
//gsoap ns2 service method-action: add ""
int ns2__add(double a, double b, double& result);
//gsoap ns2 service method-protocol: sub SOAP
//gsoap ns2 service method-style: sub rpc
//gsoap ns2 service method-encoding: sub http://schemas.xmlsoap.org/soap/encoding/
//gsoap ns2 service method-action: sub ""
int ns2__sub(double a, double b, double& result);
//gsoap ns2 service method-protocol: mul SOAP
//gsoap ns2 service method-style: mul rpc
//gsoap ns2 service method-encoding: mul http://schemas.xmlsoap.org/soap/encoding/
//gsoap ns2 service method-action: mul ""
int ns2__mul(double a, double b, double& result);

The namespace directives declare the XML schema namespace and WSDL service namespace, which are the same in this example. The name, type, and port directives declare service details, which are used by soapcpp2 to name the proxy class, the WSDL port type, and the service location port which is the endpoint URL of the service. The messaging transport mode is HTTP.

The groups of four directives per service operation declare the operation SOAP style (RPC) and encoding (SOAP encoded), and SOAP action string, which is optional and used mostly for HTTP-based routing of messages and by WS-Addressing. In this example, the protocol is SOAP 1.1 RPC encoding. More recent uses of SOAP focus on document/literal style messaging, which is also declared with directives without affecting the rest of the interface header file. For //gsoap directive details, see Section Directives .

Run soapcpp2 -j on this interface header file with the calculator service specification to generate soapcalcProxy.cpp and soapcalcProxy.h with the proxy class declaration:

#include "soapH.h"
class SOAP_CMAC calcProxy {
public:
// Context to manage proxy IO and data
struct soap *soap;
// flag indicating that this context is owned by this proxy when context is shared
bool soap_own;
// Endpoint URL of service 'calcProxy' (change as needed)
const char *soap_endpoint;
// Variables globally declared in calc.h, if any
// Construct a proxy with new managing context
calcProxy();
// Copy constructor
calcProxy(const calcProxy& rhs);
// Construct proxy given a shared managing context
calcProxy(struct soap*);
// Construct proxy given a shared managing context and endpoint URL
calcProxy(struct soap*, const char *endpoint);
// Constructor taking an endpoint URL
calcProxy(const char *endpoint);
// Constructor taking input and output mode flags for the new managing context
calcProxy(soap_mode iomode);
// Constructor taking endpoint URL and input and output mode flags for the new managing context
calcProxy(const char *endpoint, soap_mode iomode);
// Constructor taking input and output mode flags for the new managing context
// Destructor deletes non-shared managing context only (use destroy() to delete deserialized data)
virtual ~calcProxy();
// Initializer used by constructors
virtual void calcProxy_init(soap_mode imode, soap_mode omode);
// Return a copy that has a new managing context with the same engine state
virtual calcProxy *copy();
// Copy assignment
calcProxy& operator=(const calcProxy&);
// Delete all deserialized data (uses soap_destroy() and soap_end())
virtual void destroy();
// Delete all deserialized data and reset to default
virtual void reset();
// Disables and removes SOAP Header from message by setting soap->header = NULL
virtual void soap_noheader();
// Get SOAP Header structure (i.e. soap->header, which is NULL when absent)
// Get SOAP Fault structure (i.e. soap->fault, which is NULL when absent)
// Get SOAP Fault subcode QName string (NULL when absent)
virtual const char *soap_fault_subcode();
// Get SOAP Fault string/reason (NULL when absent)
virtual const char *soap_fault_string();
// Get SOAP Fault detail XML string (NULL when absent)
virtual const char *soap_fault_detail();
// Close connection (normally automatic, except for send_X ops)
virtual int soap_close_socket();
// Force close connection (can kill a thread blocked on IO)
virtual int soap_force_close_socket();
// Print fault
virtual void soap_print_fault(FILE*);
#ifndef WITH_LEAN
#ifndef WITH_COMPAT
// Print fault to stream
virtual void soap_stream_fault(std::ostream&);
#endif
// Write fault to buffer
virtual char *soap_sprint_fault(char *buf, size_t len);
#endif
// Web service operation 'add' (returns SOAP_OK or error code)
virtual int add(double a, double b, double *result)
{ return this->add(NULL, NULL, a, b, result); }
virtual int add(const char *soap_endpoint, const char *soap_action, double a, double b, double *result);
// Web service operation 'sub' (returns SOAP_OK or error code)
virtual int sub(double a, double b, double *result)
{ return this->sub(NULL, NULL, a, b, result); }
virtual int sub(const char *soap_endpoint, const char *soap_action, double a, double b, double *result);
// Web service operation 'mul' (returns SOAP_OK or error code)
virtual int mul(double a, double b, double *result)
{ return this->mul(NULL, NULL, a, b, result); }
virtual int mul(const char *soap_endpoint, const char *soap_action, double a, double b, double *result);
};

The above shows the raw source code with comments generated by soapcpp2. To obtain an annotated markdown document with documented the proxy and service classes and documented data types used by the service operations, run soapcpp2 -r option -r to generate a soapReadme.md report. This markdown file can be converted to HTML or other document formats with tools such as Doxygen.

This generated proxy class can be included into a client application together with the generated namespace table as shown in this example:

#include "soapcalcProxy.h"
#include "calc.nsmap"
int main()
{
calcProxy calc(SOAP_XML_INDENT);
double r;
if (calc.add(1.0, 2.0, r) == SOAP_OK)
std::cout << "Sum of 1.0 and 2.0 is " << r << std::endl;
else
calc.soap_stream_fault(std::cerr);
return 0;
}

The XML message sent by the client proxy:

1 <SOAP-ENV:Envelope xmlns:ns2="urn:calc" ...>
2  <SOAP-ENV:Body>
3  <ns2:add>
4  <a>1.0</a>
5  <b>2.0</b>
6  </ns2:add>
7  </SOAP-ENV:Body>
8 </SOAP-ENV:Envelope>

The XML message returned by the service:

1 <SOAP-ENV:Envelope xmlns:ns2="urn:calc" ...>
2  <SOAP-ENV:Body>
3  <ns2:addResponse>
4  <result>3.0</result>
5  </ns2:addResponse>
6  </SOAP-ENV:Body>
7 </SOAP-ENV:Envelope>

With soapcpp2 -j option -j, the constructor of the proxy class allocates and initializes a soap context as a pointer member of the class. With soapcpp2 -i option -i the proxy class is derived from the soap struct instead and this context is initialized when the proxy class constructor is invoked.

To place the proxy class in a C++ namespace name, use soapcpp2 -q name option -q name. See Section soapcpp2 options.

🔝 Back to table of contents

XSD type serialization

XML Web services use XML schemas to define the data types of XML data structures in the XML message payloads. The default encoding assumed by soapcpp2 is SOAP 1.1 document/literal style messaging but this is easily changed using options, such as -2 (SOAP 1.2), -0 (non-SOAP XML REST), and -e (SOAP with RPC encoding). See Section soapcpp2 options.

Primitive XSD types are mapped to C/C++ primitive types by means of typedef declarations in the interface header file for soapcpp2 to generate the XML data binding serialization code. A typedef binds an XML schema type name to a C/C++ type. For example:

typedef char *xsd__anyURI; // encode xsd:anyURI values as a strings
typedef std::string xsd__NMTOKEN; // encode xsd:NMTOKEN values as strings
typedef float xsd__float; // encode xsd:float values as floats

This simple mechanism informs the soapcpp2 tool to generate serializers and deserializers that explicitly encode and decode the primitive C++ types as built-in primitive XSD types. At the same time, the use of typedef does not force any source code rewriting of a client or Web service application because the internal types used by the application are not required to be changed by using this typedef mechanism.

The built-in XSD types are covered by typedef mappings and we could map XSD xsd:base64Binary and xsd:hexBinary to strings, but that would be cumbersome since the application should populate the strings properly. Instead, we can defined the following structs or classes to contain binary content that the generated serializers serialize in base64 and hexadecimal, respectively:

{
unsigned char *__ptr; // points to raw binary data
int __size; // length of raw binary data
};
{
unsigned char *__ptr; // points to raw binary data
int __size; // length of raw binary data
};

Also xsd:boolean in C can be mapped to a enum:

enum xsd__boolean { false_, true_ };

The trailing underscores are removed in XML payloads and are used here to avoid potential name clashes with C++ false and true keywords.

Annotations with typedef types introduce validation constraints that are verified by the XML parser:

typedef float ns__price "%.2g" ; // 2 fractional digits
typedef int ns__percent 0:100 ; // integer between 0 and 100
typedef std::string ns__letter "[a-z]" 1:1 ; // string of length 1 with letter (using XSD regex)
typedef unsigned long long xsd__positiveInteger 1: ; // positive integer

The soapcpp2 tool generates a schema with the following types (xsd::positiveInteger is a built-in XSD type and therefore omitted from the generated schema):

1 <simpleType name="price">
2  <restriction base="xsd:float">
3  <fractionDigits value="2"/>
4  </restriction>
5 </simpleType>
6 <simpleType name="percent">
7  <restriction base="xsd:int">
8  <minInclusive value="0"/>
9  <maxInclusive value="100"/>
10  </restriction>
11 </simpleType>
12 <simpleType name="letter">
13  <restriction base="xsd:string">
14  <pattern value="[a-z]"/>
15  <length value="1"/>
16  </restriction>
17 </simpleType>

For more details on mapping C/C++ data types with their value space constraints to XML schema and vice versa, see C and C++ XML Data Bindings documentation.

🔝 Back to table of contents

Example

Reconsider the calculator example of the previous sections, now rewritten with an explicit XSD type for double to illustrate the effect:

// Contents of file "calc.h":
typedef double xsd__double;
int ns2__add(xsd__double a, xsd__double b, xsd__double& result);

In C a pointer is used instead of a reference for the output parameter result.

The soapcpp2 tool generates the client stub function:

int soap_call_ns2__add(struct soap *soap, char *URL, char *action, double a, double b, double& result);

This means that the client application does not need to be rewritten and can still call the client stub or use the generated C++ proxy class as before.

Likewise, typedef can be used to declare user-defined schema types:

// Contents of file "calc.h":
typedef double ns2__number;
int ns2__add(ns2__number a, ns2__number b, ns2__number& result);

This lets soapcpp2 generate a WSDL and XML schema that declares the ns2:number type:

1 <schema targetNamespace="urn:calc"
2  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
3  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
4  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
6  xmlns:ns2="urn:calc"
7  xmlns="http://www.w3.org/2001/XMLSchema"
8  elementFormDefault="unqualified"
9  attributeFormDefault="unqualified">
10  <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
11  <simpleType name="number">
12  <restriction base="xsd:double">
13  </restriction>
14  </simpleType>
15 </schema>

🔝 Back to table of contents

How to change the response element name

There is no standardized convention for the response element name in a SOAP RPC encoded response message, although it is recommended that the response element name is the method name ending with "<i>`Response`</i>". For example, the response element of add is addResponse.

The response element name can be specified explicitly using a struct or class declaration in the interface header file for soapcpp2. This name should be qualified by a namespace prefix, just as the operation name should use a namespace prefix. The struct or class name represents the SOAP response element name used by the service. Consequently, the output parameter of the service operation must be declared as a member of the struct or class. The use of a struct or a class for the service response is fully SOAP 1.1/1.2 compliant. In fact, the absence of a struct or class indicates to the soapcpp2 tool to automatically generate a struct for the response which is internally used by a stub.

🔝 Back to table of contents

Example

Reconsider the calculator service operation specification which can be rewritten with an explicit declaration of a SOAP response element as follows:

// Contents of file "calc.h":
struct ns2__addResponse { double result; };
int ns2__add(double a, double b, struct ns2__addResponse& r);

This wraps the output parameters in a struct ns2__addResponse. Note that in C a pointer is used instead of a reference for the output wrapper parameter r.

In this example we just made an explicit output parameter wrapper, meaning that SOAP request and response messages will be the same as before without this wrapper:

1 <SOAP-ENV:Envelope xmlns:ns2="urn:calc" ...>
2  <SOAP-ENV:Body>
3  <ns2:addResponse>
4  <result>3.0</result>
5  </ns2:addResponse>
6  </SOAP-ENV:Body>
7 </SOAP-ENV:Envelope>

We can use any other name with a namespace prefix for the wrapper struct or class to change the response element name.

🔝 Back to table of contents

How to specify multiple output parameters

The soapcpp2 tool uses the convention that the last parameter of the function prototype declaration of a service operation in an interface header file is the output parameter of the service operation. All other parameters are considered input parameters of the service operation.

To specify a service operation with multiple output parameters, a struct or class is declared to wrap the service operation response parameters, see also Section How to change the response element name . The name of the struct or class should have a namespace prefix, just as the service method name. The members of the struct or class are the output parameters of the service operation.

The order of the input parameters in the function prototype and the order of the output parameters (the members of the wrapper struct or class) is not significant. However, the SOAP 1.1 RPC encoding specification states that input and output parameters may be treated as anonymous parameter names, which requires a particular ordering of these parameters, see Section How to specify anonymous parameter names .

🔝 Back to table of contents

Example

As an example, consider a hypothetical service operation 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);

The soapcpp2 tool takes this header file as input and generates source code for the function soap_call_ns3__getNames. When invoked by a client application, this stub function produces the XML request message:

1 <SOAP-ENV:Envelope xmlns:ns3="urn:names" ...>
2  <SOAP-ENV:Body>
3  <ns3:getNames>
4  <SSN>999 99 9999</SSN>
5  </ns3:getNames>
6  </SOAP-ENV:Body>
7 </SOAP-ENV:Envelope>

The response XML message:

1 <SOAP-ENV:Envelope xmlns:ns3="urn:names" ...>
2  <SOAP-ENV:Body>
3  <ns3:getNamesResponse>
4  <first>John</first>
5  <last>Doe</last>
6  </ns3:getNamesResponse>
7  </SOAP-ENV:Body>
8 </SOAP-ENV:Envelope>

where first and last are the output parameters wrapped in the getNamesResponse struct.

As another example, consider a service operation copy with an input parameter and an output parameter with identical parameter names (this is not prohibited by the SOAP 1.1/1.2 protocols). This can be specified using a wrapper struct for the output parameters, thus avoiding the name clash we would run into without this wrapper:

// Content of file "copy.h":
int X_rox__copy_name(char *name, struct X_rox__copy_nameResponse { char *name; } &r);

The use of a struct or class for the service operation response enables the declaration of service operations that have parameters that are passed both as input and output parameters.

The soapcpp2 tool takes the copy.h header file as input and generates the soap_call_X_rox__copy_name stub function. When invoked by a client application, the stub function produces the XML request message:

1 <SOAP-ENV:Envelope xmlns:X-rox="urn:copy" ...>
2  <SOAP-ENV:Body>
3  <X-rox:copy-name>
4  <name>hello</name>
5  </X-rox:copy-name>
6  </SOAP-ENV:Body>
7 </SOAP-ENV:Envelope>

The response XML message:

1 <SOAP-ENV:Envelope xmlns:X-rox="urn:copy" ...>
2  <SOAP-ENV:Body>
3  <X-rox:copy-nameResponse>
4  <name>SOAP</name>
5  </X-rox:copy-nameResponse>
6  </SOAP-ENV:Body>
7 </SOAP-ENV:Envelope>

The name will be parsed and decoded by the stub function and returned as the name member of the struct X_rox__copy_nameResponse &r parameter.

You can use the service operation name as a wrapper for the response:

// Content of file "copy.h":
int X_rox__copy_name(char *name, struct X_rox__copy_name &r);

where the struct X_rox__copy_name is generated by soapcpp2 automatically and does not need to be redeclared.

The response XML message in this case would be:

1 <SOAP-ENV:Envelope xmlns:X-rox="urn:copy" ...>
2  <SOAP-ENV:Body>
3  <X-rox:copy-name>
4  <name>SOAP</name>
5  </X-rox:copy-name>
6  </SOAP-ENV:Body>
7 </SOAP-ENV:Envelope>

🔝 Back to table of contents

How to specify output parameters with compound data types

If the single output parameter of a service operation is a compound data type such as a struct or class it is necessary to specify the response element of the service operation 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 Section How to change the response element name .

🔝 Back to table of contents

Example

This is best illustrated with an example. Suppose we have a Flighttracker service that provides real time flight information. It requires an airline code and flight number as parameters. The service operation name is getFlightInfo that has two string parameters: the airline code and flight number. The method returns a getFlightResponse response element with a return output parameter that is of a compound data type FlightInfo. The type FlightInfo is represented by a class in the header file:

// 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 response element ns1__getFlightInfoResponse is explicitly declared and it has one member: return_ of type ns2__FlightInfo. Note that return_ has a trailing underscore to avoid a name clash with the return keyword, see Section C/C++ identifier name to XML tag name translation for details on the translation of C/C++ identifiers to XML names.

The soapcpp2 tool generates the soap_call_ns1__getFlightInfo stub function. Here is an example fragment of a client application that uses this proxy to request flight information:

#include "soapH.h"
#include "ns1.nsmap"
int main()
{
struct soap soap;
ns2__FlightInfo flight;
if (soap_call_ns1__getFlightInfo(&soap, "testvger.objectspace.com/soap/servlet/rpcrouter", "urn:galdemo:flighttracker", "UAL", "184", flight))
soap_print_fault(&soap, stderr); // nonzero return means that an error occurred
else
cout << flight.return_.equipment << " flight " << flight.return_.airline << flight.return_.flightNumber
<< " traveling " << flight.return_.speed << " mph " << " at " << flight.return_.altitude
<< " ft, is located " << flight.return_.currentLocation << endl;
soap_destroy(&soap); // delete managed class instances
soap_end(&soap); // delete managed data and temporaries
soap_done(&soap); // finalize the context
}
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 }
};

When invoked by a client application, the stub function produces the XML request:

POST /soap/servlet/rpcrouter HTTP/1.1 
Host: testvger.objectspace.com 
Content-Type: text/xml 
Content-Length: 634 
SOAPAction: "urn:galdemo:flighttracker" 
1 <?xml version="1.0" encoding="UTF-8"?>
2 <SOAP-ENV:Envelope
3  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
4  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
5  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
7  xmlns:ns1="urn:galdemo:flighttracker"
8  xmlns:ns2="http://galdemo.flighttracker.com">
9  <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
10  <ns1:getFlightInfo>
11  <param1>UAL</param1>
12  <param2>184</param2>
13  </ns1:getFlightInfo>
14  </SOAP-ENV:Body>
15 </SOAP-ENV:Envelope>

The service responds with:

HTTP/1.1 200 OK 
Date: Thu, 30 Aug 2011 00:34:17 GMT 
Server: IBM_HTTP_Server/1.3.12.3 Apache/1.3.12 (Win32) 
Content-Length: 861 
Content-Type: text/xml; charset=utf-8 
1 <?xml version="1.0" encoding="UTF-8"?>
2 <SOAP-ENV:Envelope
3  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
4  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
6  <SOAP-ENV:Body>
7  <ns1:getFlightInfoResponse
8  xmlns:ns1="urn:galdemo:flighttracker"
9  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
10  <return xmlns:ns2="http://galdemo.flighttracker.com" xsi:type="ns2:FlightInfo">
11  <equipment xsi:type="xsd:string">A320</equipment>
12  <airline xsi:type="xsd:string">UAL</airline>
13  <currentLocation xsi:type="xsd:string">188 mi W of Lincoln, NE</currentLocation>
14  <altitude xsi:type="xsd:string">37000</altitude>
15  <speed xsi:type="xsd:string">497</speed>
16  <flightNumber xsi:type="xsd:string">184</flightNumber>
17  </return>
18  </ns1:getFlightInfoResponse>
19  </SOAP-ENV:Body>
20 </SOAP-ENV:Envelope>

The stub function returns the service response in variable flight of type struct ns1__getFlightInfoResponse and this information can be displayed by the client application:

A320 flight UAL184 traveling 497 mph at 37000 ft, is located 188 mi W of Lincoln, NE

Note that the response includes xsi:type attributes indicating the schema types of the elements in the XML message. This was common practice in earlier SOAP implementations and some SOAP implementations relied on it, but it was never mandated by the specifications. You can let soapcpp2 generate serializers that produce the xsi:type type information in XML messages with soapcpp2 -t option -t. Otherwise, the serializers will not include xsi:type attributes in the XML message payloads unless a derived type value is used in the XML payload in place of a base type, for example a derived class in place of a base class. In this way, SOAP/XML messaging implements object inheritance cleanly and efficiently because the leading xsi:type attribute value corresponding to the serialized derived class in an XML message lets an XML parser and deserializer instantiate the derived class to populate it immediately (something that can't be claimed of JSON since JSON has no attributes and object properties are unordered).

Note
: the flight tracker service is no longer available since 9/11/2001. It is kept in the documentation as an example to illustrate the use of structs/classes and response types.

🔝 Back to table of contents

How to specify anonymous parameter names

The SOAP RPC encoding protocol allows parameter names to be anonymous. That is, the name(s) of the output parameters of a service operation are not strictly required to match a client's view of the parameters names. Also, the input parameter names of a service operation are not strictly required to match a service's view of the parameter names. The soapcpp2 tool can generate stub and skeleton functions that support anonymous parameters. Parameter names are implicitly anonymous by omitting the parameter names in the function prototype of the service operation. For example:

// Contents of file "calc.h":
int ns2__add(double, double, double&);

This enumerates the parameter names as _param_1, _param_2, and _param_3, where the leading underscore makes these names anonymous, meaning that the XML parser and deserializer will accept any parameter name to extract their values, that is, even when the name of the XML element representing the parameter differs.

To make parameter names explicitly anonymous, specify parameter names that start with an underscore (_) in the function prototype in the header file.

For example:

// Contents of file "calc.h":
int ns2__add(double _a, double _b, double& _return);

In this example, the _a, _b, and _return are anonymous parameters.

Warning
When anonymous parameter names are used, the order of the parameters in the function prototype of a service operation is significant.

🔝 Back to table of contents

How to specify a service operation with no input parameters

To specify a service operation that has no input parameters, just provide a function prototype with one parameter which is the output parameter that is either a pointer or a reference, for example:

int ns__getValue(double& value);

The soapcpp2 tool generates a struct for each service operation request message, which in this case is an empty struct. To prevent C compilers from throwing an error, the empty struct is patched at compile time with the compile-time flag WITH_NOEMPTYSTRUCT.

🔝 Back to table of contents

How to specify a service operation with no output parameters

To specify a service operation that has no output parameters, just define 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);

Since the response struct is empty, no output parameters are specified. The SOAP response message has an empty response element ns:signalResponse.

Specifying an empty response is not identical to SOAP one-way messaging, which is asynchronous and does not expect an XML response message to be transmitted at all, just an empty HTTP OK response to a HTTP POST request. See Section Asynchronous one-way message passing on one-way messaging.

How to switch to REST from SOAP

To switch to RESTful Web APIs from SOAP Web services APIs is simple, just use a directive.

To declare HTTP POST as the default HTTP method to use with client-side calls for all service operations associated with the ns namespace prefix:

//gsoap ns service protocol: POST

To declare the HTTP POST method for a specific service operation, use:

//gsoap ns service protocol: POST
int ns__webmethod(...);

You can specify GET, PUT, POST, and DELETE. With GET the input parameters of the service operations should be primitive types. See Section Service directives.

🔝 Back to table of contents

How to build Web services APIs

The soapcpp2 tool generates skeleton functions in C or C++ source code for each of the service operations specified as function prototypes in the interface header file input to soapcpp2. The skeleton functions can be readily used to implement the service operations in a new Web service. The compound data types used by the input and output parameters of service operations must be declared in the interface header file, such as structs, classes, arrays, C++ containers, and pointer-based data structures (e.g. data structure trees and arbitrary operation. The soapcpp2 tool automatically generates serializers and deserializers for the data types to enable the generated skeleton functions to encode and decode the contents of the parameters of the service operations. The soapcpp2 tool also generates a service operation request dispatcher function soap_serve that serves requests by calling the appropriate skeleton.

🔝 Back to table of contents

Example

The following example specifies three service operations of a Web service:

// Contents of file "calc.h":
//gsoap ns service namespace: urn:simple-calc
int ns__add(double a, double b, double& result);
int ns__sub(double a, double b, double& result);
int ns__sqrt(double a, double& result);

The add and sub methods are intended to add and subtract two double floating point numbers stored in input parameters a and b and should return the result of the operation in the result output parameter. The sqrt method is intended to take the square root of input parameter a and to return the result in the output parameter result.

To generate the skeleton functions, the soapcpp2 tool is invoked from the command line with:

 soapcpp2 calc.h

The soapcpp2 tool generates the skeleton functions in file soapServer.cpp for the add, sub, and sqrt service operations specified in the calc.h header file. The skeleton functions are respectively soap_serve_ns__add, soap_serve_ns__sub, and soap_serve_ns__sqrt. The generated file soapC.cpp contains serializers and deserializers for the skeleton. The soapcpp2 tool also generates a service dispatcher: the soap_serve function handles client requests and dispatches the service operation requests to the appropriate skeleton functions to serve the requests. The skeleton in turn calls the service operation implementation function. The function prototype of the service operation implementation function is specified in the header file that is input to the soapcpp2 tool.

Here is an example CGI service application that uses the generated soap_serve skeleton function to handle client requests:

// Contents of file "calc.cpp":
#include "soapH.h"
#include "ns.nsmap"
#include <math.h> // for sqrt()
int main()
{
return soap_serve(soap_new());
}
// Implementation of the "add" service operation:
int ns__add(struct soap *soap, double a, double b, double& result)
{
result = a + b;
return SOAP_OK;
}
// Implementation of the "sub" service operation:
int ns__sub(struct soap *soap, double a, double b, double& result)
{
result = a - b;
return SOAP_OK;
}
// Implementation of the "sqrt" service operation:
int ns__sqrt(struct soap *soap, double a, double& result)
{
if (a < 0)
return soap_receiver_fault(soap, "Square root of negative number", "I can only take the square root of a non-negative number");
result = sqrt(a);
return SOAP_OK;
}
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:simple-calc" }, // binds "ns" namespace prefix to schema URI
{ NULL, NULL }
};

Note that the service operations have an extra input parameter which is a pointer to the soap context.

The implementation of the service operations must return SOAP_OK or a nonzero error code. The code SOAP_OK denotes success. A nonzero error code is returned with soap_receiver_fault and soap_sender_fault. These functions also set up a SOAP Fault structure with the details of the fault returned. This is done by setting the soap::fault which points to SOAP_ENV__Fault structure. Its SOAP_ENV__Fault::faultstring string and SOAP_ENV__Fault::detaildetails are populated with the fault string (XML text) and fault detail (XML string). SOAP 1.2 requires the SOAP_ENV__Fault::SOAP_ENV__Reason and the SOAP_ENV__Fault::SOAP_ENV__Detail strings to be assigned instead.

This service application can be readily installed as a CGI application, which is a simple stateless way to deploy services. To deploy this service as a multi-threaded stand-alone server application see Sections How to create a stand-alone server and How to create a multi-threaded stand-alone service.

Besides generating the skeleton functions and serializers in source code, the soapcpp2 tool also generates a WSDL file for this service, see Section How to generate WSDL service descriptions for details on WSDL.

As per SOAP protocol (when applicable), "SOAP actions" are HTTP headers that are specific to the SOAP protocol and provide a means for routing service requests and for security reasons, for example firewall software can inspect SOAP action headers to grant or deny the SOAP request. Use soapcpp2 -a option -a or soapcpp2 -A option -A to let the generated skeleton functions dispatch the requests based on the SOAP action HTTP header together with (or instead) the name of the XML request element.

Note that soapcpp2 generates both clients and services based on the interface header file input. Which means that there is no need to modify the interface header file for client side or server side deployments. For example, the generated soap_call_ns__add stub function is saved to the soapClient.cpp file after invoking the soapcpp2 tool on the calc.h header file.

🔝 Back to table of contents

MSVC++ builds

  • Win32 builds require winsock2 (MS Visual C++ ws2_32.lib) To do this in Visual C++, go to Project, Properties, select Link and add ws2_32.lib to the Object library modules entry.
  • Use files with extension .cpp only. This means that you may have to rename all .c files to .cpp.
  • Turn pre-compiled headers off.
  • When creating a new project, you can specify a custom build step to automatically invoke the soapcpp2 tool on a interface header file for soapcpp2. In this way you can incrementally build a new service by adding new operations and data types to the header file. For the latest instruction on how to specify a custom build step to run soapcpp2, see the gSOAP download and installation page https://www.genivia.com/downloads.html.
  • You may want to use the ISAPI extension or WinInet plugin available in the gsoap/mod_gsoap directory of the gSOAP package to simplify Internet access and deal with encryption, proxies, and authentication. See the gSOAP ISAPI extension documentation and the gSOAP WinInet plugin documentation.

🔝 Back to table of contents

How to create a stand-alone server

The deployment of a Web service as a CGI application is an easy means to provide your service on the Internet. However, CGI is stateless and the performance of CGI is not great. Instead, gSOAP services can be run as stand-alone services on any port by using the built-in HTTP and TCP/IP stacks of the engine. The recommended mechanism to deploy a service is through the gSOAP Apache module or ISAPI extension. These servers and modules are designed for server load balancing and access control. See the gSOAP Apache module documentation and the gSOAP ISAPI extension documentation for details.

See also the getting started page https://www.genivia.com/dev.html and tutorial page https://www.genivia.com/tutorials.html to get started with developing client and stand-alone server applications using the gSOAP tools.

To create a stand-alone service, the main function of the service application should call soap_bind to bind a port and then loop over soap_accept to accept requests to serve with soap_serve. Also call soap_ssl_accept when HTTPS is used, which is set up with soap_ssl_server_context.

For example:

#include "soapH.h"
#include "ns.nsmap"
int main()
{
struct soap *soap = soap_new1(SOAP_XML_INDENT);
soap->send_timeout = 10; // 10 seconds max socket delay
soap->recv_timeout = 10; // 10 seconds max socket delay
soap->accept_timeout = 3600; // server stops after 1 hour of inactivity
soap->max_keep_alive = 100; // max keep-alive sequence
// soap_ssl_server_context(soap, ...); // call this function when HTTPS is used
SOAP_SOCKET m, s; // master and accepting sockets
m = soap_bind(soap, NULL, 18083, 10); // small BACKLOG for iterative servers
{
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);
{
soap_print_fault(soap, stderr);
break;
}
// soap_ssl_accept(soap); // call when HTTPS is used
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_print_fault(soap, stderr);
else
fprintf(stderr, "request served\n");
soap_destroy(soap); // delete managed class instances
soap_end(soap); // delete managed data and temporaries
}
}
soap_free(soap); // finalize and delete the context
}

The soap_serve dispatcher handles one request or multiple requests when HTTP keep-alive is enabled with the SOAP_IO_KEEPALIVE flag, which should only be used with client applications or with stand-alone multi-threaded services, see the next section and Section TCP and HTTP keep-alive.

The gSOAP functions that are frequently used for server-side coding are:

  • soap_bind(struct soap *soap, char *host, int port, int backlog) binds soap::master socket to the specified port and host name (or NULL for the current machine), using a backlog queue size of pending requests, returns master socket. We check the return value with soap_valid_socket. The backlog queue size should be small, say 2 to 10, for iterative (not multi-threaded) stand-alone servers to ensure fairness among connecting clients. A smaller value increases fairness and defends against denial of service, but hampers performance because connection requests may be refused.
  • soap_accept(struct soap *soap) returns SOAP_SOCKET socket soap::socket when connected. We check the return value with soap_valid_socket.
  • soap_ssl_accept(struct soap *soap) returns SOAP_OK when the HTTPS handshake successfully completed.

The IPv4 address is stored in soap::ip. If IPv6 is enabled with WITH_IPV6 then soap::ip6 contains the IPv6 address.

The soap::accept_timeout context variable of the context specifies the timeout value for a non-blocking soap_accept call. See Section Timeout management for non-blocking operations for more details on timeout management.

The soap_serve function parses the inbound HTTP request and dispatches the request to the skeleton functions that call the service operations implemented.

See Section Memory management for more details on memory management.

🔝 Back to table of contents

How to create a multi-threaded stand-alone service

Stand-alone multi-threading a Web Service is essential when the response times for handling requests by the service are long or when HTTP keep-alive is enabled, see also Section TCP and HTTP keep-alive .

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 and server remain connected until 100 (SOAP_MAXKEEPALIVE) request-response iterations later as specified by soap::max_keep_alive or when a timeout occurred. Thereby preventing other clients from connecting.

The recommended mechanism to deploy a service is through the gSOAP Apache module or ISAPI extension. These servers and modules are designed for server load balancing and access control. See the gSOAP Apache module documentation and the gSOAP ISAPI extension documentation for details.

See also the getting started page https://www.genivia.com/dev.html and tutorial page https://www.genivia.com/tutorials.html to get started with developing client and stand-alone server applications using the gSOAP tools.

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 "ns.nsmap"
#include "plugin/threads.h"
#define BACKLOG (100) // Max request backlog of pending requests
int main(int argc, char **argv)
{
struct soap soap;
soap_init(&soap);
if (argc < 2) // no args: assume this is a CGI application
{
soap_serve(&soap); // serve request, one thread, CGI style
soap_destroy(&soap); // delete managed class instances
soap_end(&soap); // delete managed data and temporaries
}
else
{
void *process_request(void*);
struct soap *tsoap;
int port = atoi(argv[1]); // first command-line arg is port
SOAP_SOCKET m, s;
soap.send_timeout = 10; // 10 seconds max socket delay
soap.recv_timeout = 10; // 10 seconds max socket delay
soap.accept_timeout = 3600; // server stops after 1 hour of inactivity
soap.max_keep_alive = 100; // max keep-alive sequence
m = soap_bind(&soap, NULL, port, BACKLOG);
exit(EXIT_FAILURE);
fprintf(stderr, "Socket connection successful %d\n", m);
while (1)
{
s = soap_accept(&soap);
{
fprintf(stderr, "Accept socket %d connection from IP %d.%d.%d.%d\n", s, (soap.ip>>24)&0xFF, (soap.ip>>16)&0xFF, (soap.ip>>8)&0xFF, soap.ip&0xFF);
tsoap = soap_copy(&soap); // make a copy
if (!tsoap)
else
while (THREAD_CREATE(&tid, (void*(*)(void*))process_request, (void*)tsoap))
sleep(1); // failed, try again
}
else if (soap.errnum) // accept failed, try again after 1 second
{
soap_print_fault(&soap, stderr);
sleep(1);
}
else
{
fprintf(stderr, "server timed out\n");
break;
}
}
}
soap_done(&soap); // finalize context
return 0;
}
void *process_request(void* tsoap)
{
struct soap *soap = (struct soap*)tsoap;
soap_serve(soap);
soap_destroy(soap); // delete managed class instances
soap_end(soap); // delete managed data and temporaries
soap_free(soap); // finalize and delete the context
return NULL;
}
// ... the service operations are defined here ...

For this multi-threaded application the gsoap/plugin/threads.h and gsoap/plugin/threads.c portable threads and mutex API is used.

The server spawns a thread per request. Each thread executes a soap_serve using a copy of the soap context created with soap_copy (if soap_copy fails due to out of memory then we can still recover as shown, recovery from errors is an important aspect of gSOAP's design and API implementation). Note that the server does not wait for threads to join the main thread upon program termination.

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 limits the number of concurrent threads to reduce the machine's CPU resource utilization:

#include "soapH.h"
#include "ns.nsmap"
#include "plugin/threads.h"
#define BACKLOG (100) // Max request backlog of pending requests
#define MAX_THR (10) // Max threads to serve requests
int main(int argc, char **argv)
{
struct soap soap;
soap_init(&soap);
if (argc < 2) // no args: assume this is a CGI application
{
soap_serve(&soap); // serve request, one thread, CGI style
soap_destroy(&soap); // delete managed class instances
soap_end(&soap); // delete managed data and temporaries
}
else
{
struct soap *soap_thr[MAX_THR]; // each thread needs a context
THREAD_TYPE tid[MAX_THR]; // array of thread IDs
int port = atoi(argv[1]); // first command-line arg is port
SOAP_SOCKET m, s;
int i;
soap.send_timeout = 10; // 10 seconds max socket delay
soap.recv_timeout = 10; // 10 seconds max socket delay
soap.accept_timeout = 3600; // server stops after 1 hour of inactivity
soap.max_keep_alive = 100; // max keep-alive sequence
m = soap_bind(&soap, NULL, port, BACKLOG);
exit(EXIT_FAILURE);
fprintf(stderr, "Socket connection successful %d\n", m);
for (i = 0; i < MAX_THR; i++)
soap_thr[i] = NULL;
while (1)
{
for (i = 0; i < MAX_THR; i++)
{
s = soap_accept(&soap);
{
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(EXIT_FAILURE); // could not allocate
}
else // recycle threaded soap contexts
{
// optionally, we can cancel the current thread when stuck on IO:
// soap_close_connection(soap_thr[i]); // requires compiling 2.8.71 or greater with -DWITH_SELF_PIPE
THREAD_JOIN(tid[i]);
fprintf(stderr, "Thread %d completed\n", i);
soap_destroy(soap_thr[i]); // delete managed class instances of thread
soap_end(soap_thr[i]); // delete managed data and temporaries of thread
soap_copy_stream(soap_thr[i], &soap); // pass the connection on to the thread
}
while (THREAD_CREATE(&tid[i], (void*(*)(void*))soap_serve, (void*)soap_thr[i]))
sleep(1); // failed, try again
}
else if (soap.errnum) // accept failed, try again after 1 second
{
soap_print_fault(&soap, stderr);
sleep(1);
}
else
{
fprintf(stderr, "Server timed out\n");
goto end;
}
}
}
end:
for (i = 0; i < MAX_THR; i++)
{
if (soap_thr[i])
{
THREAD_JOIN(tid[i]);
soap_destroy(soap_thr[i]);
soap_end(soap_thr[i]);
soap_free(soap_thr[i]);
}
}
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
// ... the service operations are defined here ...

The advantage of the code shown above is that the machine cannot be overloaded with requests, since the number of active services is limited. However, threads are still started and terminated. This overhead can be eliminated using a queue of requests (a queue of accepted socket connections):

#include "soapH.h"
#include "ns.nsmap"
#include "plugin/threads.h"
#define BACKLOG (100) // Max. request backlog of pending requests
#define MAX_THR (64) // Size of thread pool
#define MAX_QUEUE (1000) // Max. size of request queue
void *process_queue(void*);
int enqueue(SOAP_SOCKET);
SOAP_SOCKET dequeue();
static SOAP_SOCKET queue[MAX_QUEUE]; // The global request queue of sockets
static int head = 0, tail = 0;
static MUTEX_TYPE queue_lock; // mutex for queue ops critical sections
static COND_TYPE queue_notempty; // condition variable when queue is empty
static COND_TYPE queue_notfull; // condition variable when queue is full
int main(int argc, char **argv)
{
struct soap soap;
soap_init(&soap);
if (argc < 2) // no args: assume this is a CGI application
{
soap_serve(&soap); // serve request, one thread, CGI style
soap_destroy(&soap); // delete managed class instances
soap_end(&soap); // delete managed data and temporaries
}
else
{
struct soap *soap_thr[MAX_THR]; // each thread needs a context
THREAD_TYPE tid[MAX_THR];
int port = atoi(argv[1]); // first command-line arg is port
SOAP_SOCKET m, s;
int i;
m = soap_bind(&soap, NULL, port, BACKLOG);
exit(EXIT_FAILURE);
fprintf(stderr, "Socket connection successful %d\n", m);
MUTEX_SETUP(queue_lock);
COND_SETUP(queue_notempty);
COND_SETUP(queue_notfull);
for (i = 0; i < MAX_THR; i++)
{
soap_thr[i] = soap_copy(&soap);
fprintf(stderr, "Starting thread %d\n", i);
while (THREAD_CREATE(&tid[i], (void*(*)(void*))process_queue, (void*)soap_thr[i]))
sleep(1); // failed, try again
}
while (1)
{
s = soap_accept(&soap);
{
fprintf(stderr, "Accept socket %d connection from IP %d.%d.%d.%d\n", s, (soap.ip>>24)&0xFF, (soap.ip>>16)&0xFF, (soap.ip>>8)&0xFF, soap.ip&0xFF);
enqueue(s);
}
else if (soap.errnum) // accept failed, try again after 1 second
{
soap_print_fault(&soap, stderr);
sleep(1);
}
else
{
fprintf(stderr, "Server timed out\n");
break;
}
}
for (i = 0; i < MAX_THR; i++)
for (i = 0; i < MAX_THR; i++)
{
fprintf(stderr, "Waiting for thread %d to terminate... ", i);
THREAD_JOIN(tid[i]);
fprintf(stderr, "terminated\n");
soap_free(soap_thr[i]);
}
COND_CLEANUP(queue_notfull);
COND_CLEANUP(queue_notempty);
MUTEX_CLEANUP(queue_lock);
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
void *process_queue(void *tsoap)
{
struct soap *soap = (struct soap*)tsoap;
while (1)
{
soap->socket = dequeue();
if (!soap_valid_socket(soap->socket))
break;
soap_serve(soap);
soap_destroy(soap);
soap_end(soap);
fprintf(stderr, "served\n");
}
soap_free(soap);
return NULL;
}
/* add job (socket with pending request) to queue */
void enqueue(SOAP_SOCKET s)
{
int next;
MUTEX_LOCK(queue_lock);
next = (tail + 1) % MAX_QUEUE;
if (next == head)
COND_WAIT(queue_notfull, queue_lock);
queue[tail] = s;
tail = next;
COND_SIGNAL(queue_notempty);
MUTEX_UNLOCK(queue_lock);
}
/* remove job (socket with request) from queue */
SOAP_SOCKET dequeue()
{
MUTEX_LOCK(queue_lock);
if (head == tail)
COND_WAIT(queue_notempty, queue_lock);
s = queue[head];
head = (head + 1) % MAX_QUEUE;
COND_SIGNAL(queue_notfull);
MUTEX_UNLOCK(queue_lock);
return s;
}
// ... the service operations are defined here ...

For this multi-threaded application the gsoap/plugin/threads.h and gsoap/plugin/threads.c portable threads and mutex API is used.

🔝 Back to table of contents

How to pass application state info to service operations

The void* soap::user variable can be used to pass application state information to service operations and to plugins. This variable can be set before the soap_serve call. The service method can access this variable to use the application-dependent data. The following example shows how a non-static database handle is initialized and passed to the service methods:

int main()
{
struct soap soap;
database_handle_type database_handle;
soap_init(&soap);
soap.user = (void*)database_handle;
... //
if (soap_serve(&soap))
... // error
... //
}
int ns__webmethod(struct soap *soap, ...)
{
fetch((database_handle_type*)soap->user); // use database handle
... //
return SOAP_OK;
}

Another way to maintain and pass state information with the context is done with plugins, see Section Plugins, modules, and extensions .

🔝 Back to table of contents

How to generate C++ server classes

Server object classes for C++ server applications are automatically generated by the soapcpp2 tool using soapcpp2 -j option -j or soapcpp2 -i option -i. Without these options the soapcpp2 tool generates C-based stub and skeleton functions.

We illustrate the use of server classes with the following example interface header file:

// Content of file "calc.h":
//gsoap ns service name: Calculator
//gsoap ns service protocol: SOAP1.2
//gsoap ns service style: document
//gsoap ns service encoding: literal
//gsoap ns service location: http://www.cs.fsu.edu/~engelen/calc.cgi
//gsoap ns schema namespace: urn:calc
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);

The directives provide the service name which is used to name the service class, the protocol (SOAP 1.2) and style (document/literal), service location (endpoint URL), and the schema namespace URI.

We run soapcpp2 -i calc.h with option -i to generate soapCalculatorService.h which declares the C++ sever class that has the following structure:

#include "soapH.h"
class CalculatorService : public soap
{
public:
Calculator() { soap_init(this); };
... // more constructors, elided here for clarity
~Calculator() { soap_done(this); };
SOAP_SOCKET bind(const char *host, int port, int backlog) { return soap_bind(this, host, port, backlog); }
SOAP_SOCKET accept() { return soap_accept(this); }
int serve() { return soap_serve(this); };
void destroy() { soap_destroy(this); soap_end(this); }
... // more methods, elided here for clarity
// user-defined service operations:
int add(double a, double b, double& result);
int sub(double a, double b, double& result);
int mul(double a, double b, double& result);
int div(double a, double b, double& result);
};

This generated server class serve method calls the add, sub, mul, and div methods upon receiving an XML request message. These methods should be implemented, for example as follows in a CGI-based service:

#include "soapCalculatorService.h"
#include "Calculator.nsmap"
int main()
{
CalculatorService calc;
calc.serve();
calc.destroy();
}
int calc::add(double a, double b, double& result)
{
result = a + b;
return SOAP_OK;
}
int calc::sub(double a, double b, double& result)
{
result = a - b;
return SOAP_OK;
}
int calc::mul(double a, double b, double& result)
{
result = a * b;
return SOAP_OK;
}
int calc::div(double a, double b, double& result)
{
if (b == 0.0)
return soap_sender_fault(this, "Division by zero", NULL);
result = a / b;
return SOAP_OK;
}

If we run soapcpp2 -j calc.h with option -j to generate soapCalculatorService.h then we get the same class as with option -i but the soap context is not a base class but is a member of the service class:

#include "soapH.h"
class CalculatorService
{
public:
struct soap *soap;
Calculator() { soap = soap_new(); };
... // more constructors, elided here for clarity
~Calculator() { soap_free(soap); };
SOAP_SOCKET bind(const char *host, int port, int backlog) { return soap_bind(soap, host, port, backlog); }
SOAP_SOCKET accept() { return soap_accept(soap); }
int serve() { return soap_serve(soap); };
void destroy() { soap_destroy(soap); soap_end(soap); }
... // more methods, elided here for clarity
// user-defined service operations:
int add(double a, double b, double& result);
int sub(double a, double b, double& result);
int mul(double a, double b, double& result);
int div(double a, double b, double& result);
};

The only difference we make to implement the service application is to use the soap member of the class instead of this when referring to the context, which in our example changes only one line of code in the div method:

if (b == 0.0)
return soap_sender_fault(soap, "Division by zero", NULL);
}

In fact, the service classes have soap_sender_fault and soap_receiver_fault methods that can be used instead.

You can declare a C++ namespace name with soapcpp2 -q name to create a server class in the name namespace, see Section How to build a client or server in a C++ code namespace . For more options, see also Sections soapcpp2 options and How to create client/server libraries.

The example above serves CGI requests. The generated service classes also have bind and accept methods, which can be used to implement stand-alone services, see also Section How to create a stand-alone server .

A better alternative is to use soapcpp2 -j option -j or option -i. With option -j the C++ proxy and service classes have a soap context pointer. This context pointer can be set and shared among many proxy and service classes. With option -i the C++ proxy and server classes are derived from the soap context, which simplifies the proxy invocation and service operation implementations.

Compilation of the above header file with soapcpp2 -i creates new files soapCalculatorService.h and soapCalculatorService.cpp (rather than the C-style soapServer.cpp).

This generated server object class can be included into a server application together with the generated namespace table as shown in this example:

#include "soapCalculatorService.h" // get server object
#include "Calculator.nsmap" // include the generated namespace table
int main()
{
soapCalculatorService c;
return c.serve(); // calls soap_serve to serve as CGI application (using stdin/out)
}
// The 'add' service method (soapcpp2 w/ option -i)
int soapCalculatorService::add(double a, double b, double &result)
{
result = a + b;
return SOAP_OK;
}
... // sub(), mul(), and div() implementations

Note that the service operation does not need a prefix (ns__) and there is no soap context passed to the service operation since the service object itself is the context (it is derived from the soap context struct).

🔝 Back to table of contents

How to chain C++ server classes to accept messages on the same port

When combining multiple services into one application, you can run wsdl2h on multiple WSDLs to generate the single all-inclusive service definitions interface header file for soapcpp2. This header file is then processed with soapcpp2 to generate skeleton functions in C or server classes in C++ when using soapcpp2 -j option -j (or option -i).

This approach works well for C and C++ too, but the problem in C++ is that we end up with multiple service classes, each for a collection of service operations that the class is supposed to implement. But what if we need to provide one endpoint port for all services and operations? In this case invoking the server object's serve method is not sufficient, since only one service can accept requests while we want multiple services to listen to the same port.

For example, say we have three service classes soapABCService, soapUVWService, and soapXYZService. We run soapcpp2 -i -S -q name three times (on the same interface file when applicable):

soapcpp2 -i -S -qAbc file.h
soapcpp2 -i -S -qUvw file.h
soapcpp2 -i -S -qXyz file.h

To generate the common envH.h file for SOAP Header and SOAP Fault definitions is done on a env.h file that is empty or has the SOAP Header and SOAP Fault detail structures SOAP_ENV__Header and SOAP_ENV__Detail specified:

soapcpp2 -CSL -penv env.h

The approach is to chain the service dispatchers, as shown below:

#include "AbcABCService.h"
#include "UvwUVWService.h"
#include "XyzXYZService.h"
#include "envH.h" // include this file last, if this file is needed
int main()
{
Abc::soapABCService abc; // generated with soapcpp2 -i -S -qAbc
Uvw::soapUVWService uvw; // generated with soapcpp2 -i -S -qUvw
Xyz::soapXYZService xyz; // generated with soapcpp2 -i -S -qXyz
if (!soap_valid_socket(abc.bind(NULL, 8080, 100)))
exit(EXIT_FAILURE);
while (1)
{
if (!soap_valid_socket(abc.accept()))
exit(EXIT_FAILURE);
// abc.ssl_accept(); // when HTTPS is used
... //
if (soap_begin_serve(&abc)) // available in 2.8.2 and later
{
abc.soap_stream_fault(std::cerr);
}
else if (abc.dispatch() == SOAP_NO_METHOD)
{
soap_copy_stream(&uvw, &abc);
soap_free_stream(&abc); // abc no longer uses this stream
if (uvw.dispatch() == SOAP_NO_METHOD)
{
soap_copy_stream(&xyz, &uvw);
soap_free_stream(&uvw); // uvw no longer uses this stream
if (xyz.dispatch())
{
soap_send_fault(&xyz); // send fault to client
xyz.soap_stream_fault(std::cerr);
}
xyz.destroy();
}
else
{
soap_send_fault(&uvw); // send fault to client
uvw.soap_stream_fault(std::cerr);
}
uvw.destroy();
}
else
{
abc.soap_stream_fault(std::cerr);
}
abc.destroy();
}
}

The dispatch method parses the SOAP/XML request and invokes the service operations, unless there is no matching operation and SOAP_NO_METHOD is returned. The soap_copy_stream ensures that the service object uses the currently open socket. The copied streams are freed with soap_free_stream. Do not enable keep-alive support, as the socket may stay open indefinitely afterwards as a consequence. Also, the dispatch method does not send a fault to the client, which has to be explicitly done with the soap_send_fault operation when an error occurs.

In this way, multiple services can be chained to accept messages on the same port. This approach also works with SSL for HTTPS services.

However, this approach is not recommended for certain plugins, because plugins must be registered with all service objects and some plugins require state information to be used across the service objects, which will add significantly to the complexity.

Therefore, it is best to have all services share the same context. This means that soapcpp2 -j with option -j should be used instead of option -i. As a result, we can make each service class instance to share the same soap context and the same plugins.

soapcpp2 -j -S -qAbc file.h
soapcpp2 -j -S -qUvw file.h
soapcpp2 -j -S -qXyz file.h

Chaining the services is also simpler to implement since we use one soap context:

#include "AbcABCService.h"
#include "UvwUVWService.h"
#include "XyzXYZService.h"
#include "envH.h" // include this file last, if it is needed
int main()
{
struct soap *soap = soap_new();
Abc::soapABCService abc(soap); // generated with soapcpp2 -j -S -qAbc
Uvw::soapUVWService uvw(soap); // generated with soapcpp2 -j -S -qUvw
Xyz::soapXYZService xyz(soap); // generated with soapcpp2 -j -S -qXyz
if (!soap_valid_socket(soap_bind(soap, NULL, 8080, BACKLOG)))
exit(EXIT_FAILURE);
while (1)
{
exit(EXIT_FAILURE);
if (soap_begin_serve(soap))
{
soap_stream_fault(soap, std::cerr);
}
else if (abc.dispatch() == SOAP_NO_METHOD)
{
if (uvw.dispatch() == SOAP_NO_METHOD)
{
if (xyz.dispatch() == SOAP_NO_METHOD)
soap_send_fault(soap); // send fault to client
}
}
soap_destroy(soap);
soap_end(soap);
}
soap_free(soap); // safe to delete when abc, uvw, xyz are also deleted
}

However, the while loop iterates for each new connection that is established with soap_accept and does not allow for HTTP keep-alive connections to persist. For our final improvement we want to support HTTP keep-alive connections that require looping over the service dispatches until the connection closes on either end, after which we resume the outer loop. The resulting code is very close to the soapcpp2-generated soap_serve code and the serve service class methods, with the addition of the chain of service dispatches in the loop body:

#include "AbcABCService.h"
#include "UvwUVWService.h"
#include "XyzXYZService.h"
#include "envH.h" // include this file last, if it is needed
int main()
{
struct soap *soap = soap_new();
Abc::soapABCService abc(soap); // generated with soapcpp2 -j -S -qAbc
Uvw::soapUVWService uvw(soap); // generated with soapcpp2 -j -S -qUvw
Xyz::soapXYZService xyz(soap); // generated with soapcpp2 -j -S -qXyz
if (!soap_valid_socket(soap_bind(soap, NULL, 8080, BACKLOG)))
exit(EXIT_FAILURE);
while (1)
{
exit(EXIT_FAILURE);
soap->keep_alive = soap->max_keep_alive + 1; // max keep-alive iterations
do
{
if ((soap->keep_alive > 0) && (soap->max_keep_alive > 0))
soap->keep_alive--;
if (soap_begin_serve(soap))
{
if (soap->error >= SOAP_STOP) // if a plugin has served the request
continue; // then continue with the next request
break; // an error occurred
}
if (abc.dispatch() == SOAP_NO_METHOD)
{
if (uvw.dispatch() == SOAP_NO_METHOD)
{
if (xyz.dispatch() == SOAP_NO_METHOD)
soap_send_fault(soap); // send fault to client
}
}
soap_destroy(soap);
soap_end(soap);
} while (soap->keep_alive);
soap_destroy(soap);
soap_end(soap);
}
soap_free(soap); // safe to delete when abc, uvw, xyz are also deleted
}

🔝 Back to table of contents

How to generate WSDL service descriptions

The soapcpp2 tool generates WSDL (Web Service Description Language) service descriptions and XML schema files (XSDs) when processing an interface header file that wasn't generated with wsdl2h. The soapcpp2 tool produces one WSDL file for a set of service operations in the header file. If the header file has no service operations (i.e. no function prototypes) then no WSDL will be generated. The names of the function prototypes of the service operations must use the same namespace prefix and the namespace prefix is used to name the WSDL file. The WSDL file and services can be named with a //gsoap <prefix> service name: directive to specify a service name for each namespace prefix.

If multiple namespace prefixes are used to define service operations, then multiple WSDL files will be created and each file describes the set of service operations belonging to that namespace prefix.

The soapcpp2 tool also generates XML schema files (XSD files) for all serializable C/C++ types declared in the interface header file input to soapcpp2. These XSD files do not have to be published as the WSDL file already contains the appropriate XML Schema definitions.

To customize the WSDL output, use //gsoap directives to declare the service name, the endpoint port, and namespace etc:

//gsoap ns service name: example
//gsoap ns service type: examplePortType
//gsoap ns service port: http://www.example.com/example
//gsoap ns service namespace: urn:example

These are some examples and defaults will be used when directives are not specified. Recommended is to specify at least the service name and namespace URI. More details and settings for the service can be declared as well. See Section Directives for more details.

In addition to the generation of the WSDL files, a file with a namespace mapping table is generated by the gSOAP soapcpp2 tool. 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", "urn:example" }, // binds "ns" namespace prefix to schema URI
{ NULL, NULL }
};

This file should be included in the client or service application, see Section XML namespaces and the namespace mapping table for details on namespace mapping tables.

🔝 Back to table of contents

Example

For example, suppose the following service operations are defined in the calc.h header file:

// Content of file "calc.h":
//gsoap ns service name: calc
int ns__add(double a, double b, double& result);
int ns__sub(double a, double b, double& result);
int ns__sqrt(double a, double& result);

One WSDL file calc.wsdl will be generated that describes the three service operations:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <definitions name="Service"
3  targetNamespace="http://tempuri.org/ns.xsd/Service.wsdl"
4  xmlns:tns="http://tempuri.org/ns.xsd/Service.wsdl"
5  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
6  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
7  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
8  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
9  xmlns:ns="http://tempuri.org/ns.xsd"
10  xmlns:SOAP="http://schemas.xmlsoap.org/wsdl/soap/"
11  xmlns:HTTP="http://schemas.xmlsoap.org/wsdl/http/"
12  xmlns:MIME="http://schemas.xmlsoap.org/wsdl/mime/"
13  xmlns:DIME="http://schemas.xmlsoap.org/ws/2002/04/dime/wsdl/"
14  xmlns:WSDL="http://schemas.xmlsoap.org/wsdl/"
15  xmlns="http://schemas.xmlsoap.org/wsdl/">
16 
17 <types>
18 
19  <schema targetNamespace="http://tempuri.org/ns.xsd"
20  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
21  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
22  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
23  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
24  xmlns:ns="http://tempuri.org/ns.xsd"
25  xmlns="http://www.w3.org/2001/XMLSchema"
26  elementFormDefault="unqualified"
27  attributeFormDefault="unqualified">
28  <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
29 
30  <element name="add">
31  <complexType>
32  <sequence>
33  <element name="a" type="xsd:double" minOccurs="1" maxOccurs="1"/>
34  <element name="b" type="xsd:double" minOccurs="1" maxOccurs="1"/>
35  </sequence>
36  </complexType>
37  </element>
38 
39  <element name="addResponse">
40  <complexType>
41  <sequence>
42  <element name="result" type="xsd:double" minOccurs="1" maxOccurs="1"/>
43  </sequence>
44  </complexType>
45  </element>
46 
47  <element name="sub">
48  <complexType>
49  <sequence>
50  <element name="a" type="xsd:double" minOccurs="1" maxOccurs="1"/>
51  <element name="b" type="xsd:double" minOccurs="1" maxOccurs="1"/>
52  </sequence>
53  </complexType>
54  </element>
55 
56  <element name="subResponse">
57  <complexType>
58  <sequence>
59  <element name="result" type="xsd:double" minOccurs="1" maxOccurs="1"/>
60  </sequence>
61  </complexType>
62  </element>
63 
64  <element name="sqrt">
65  <complexType>
66  <sequence>
67  <element name="a" type="xsd:double" minOccurs="1" maxOccurs="1"/>
68  </sequence>
69  </complexType>
70  </element>
71 
72  <element name="sqrtResponse">
73  <complexType>
74  <sequence>
75  <element name="result" type="xsd:double" minOccurs="1" maxOccurs="1"/>
76  </sequence>
77  </complexType>
78  </element>
79  </schema>
80 
81 </types>
82 
83 <message name="addRequest">
84  <part name="Body" element="ns:add"/>
85 </message>
86 
87 <message name="addResponse">
88  <part name="Body" element="ns:addResponse"/>
89 </message>
90 
91 <message name="subRequest">
92  <part name="Body" element="ns:sub"/>
93 </message>
94 
95 <message name="subResponse">
96  <part name="Body" element="ns:subResponse"/>
97 </message>
98 
99 <message name="sqrtRequest">
100  <part name="Body" element="ns:sqrt"/>
101 </message>
102 
103 <message name="sqrtResponse">
104  <part name="Body" element="ns:sqrtResponse"/>
105 </message>
106 
107 <portType name="ServicePortType">
108  <operation name="add">
109  <documentation>Service definition of function ns__add</documentation>
110  <input message="tns:addRequest"/>
111  <output message="tns:addResponse"/>
112  </operation>
113  <operation name="sub">
114  <documentation>Service definition of function ns__sub</documentation>
115  <input message="tns:subRequest"/>
116  <output message="tns:subResponse"/>
117  </operation>
118  <operation name="sqrt">
119  <documentation>Service definition of function ns__sqrt</documentation>
120  <input message="tns:sqrtRequest"/>
121  <output message="tns:sqrtResponse"/>
122  </operation>
123 </portType>
124 
125 <binding name="Service" type="tns:ServicePortType">
126  <SOAP:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
127  <operation name="add">
128  <SOAP:operation soapAction=""/>
129  <input>
130  <SOAP:body use="literal" parts="Body"/>
131  </input>
132  <output>
133  <SOAP:body use="literal" parts="Body"/>
134  </output>
135  </operation>
136  <operation name="sub">
137  <SOAP:operation soapAction=""/>
138  <input>
139  <SOAP:body use="literal" parts="Body"/>
140  </input>
141  <output>
142  <SOAP:body use="literal" parts="Body"/>
143  </output>
144  </operation>
145  <operation name="sqrt">
146  <SOAP:operation soapAction=""/>
147  <input>
148  <SOAP:body use="literal" parts="Body"/>
149  </input>
150  <output>
151  <SOAP:body use="literal" parts="Body"/>
152  </output>
153  </operation>
154 </binding>
155 
156 <service name="Service">
157  <documentation>gSOAP 2.8.70 generated service definition</documentation>
158  <port name="Service" binding="tns:Service">
159  <SOAP:address location="http://localhost:80"/>
160  </port>
161 </service>
162 
163 </definitions>

The above uses the default settings for the service name, port, and namespace which can be set in the header file with //gsoap directives, see Section Directives .

🔝 Back to table of contents

How to make client-side calls within a service operation

Invoking a server-side client call requires the use of a new soap context in the service operation itself, which is best illustrated with an example. The following example 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 services. The currency-converted quote is then calculated and returned.

In addition to being a client of two Web 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 soapcpp2 tool is given below. The example is for illustrative purposes only because the XMethods stock quote and currency rate services are no longer operational:

// Contents of file "quotex.h":
// the first service: stock quotes
//gsoap ns1 service namespace: urn:xmethods-delayed-quotes
//gsoap ns1 service style: rpc
//gsoap ns1 service encoding: encoded
int ns1__getQuote(char *symbol, float& result);
// the second service: currency exchange
//gsoap ns2 service namespace: urn:xmethods-CurrencyExchange
//gsoap ns2 service style: rpc
//gsoap ns2 service encoding: encoded
int ns2__getRate(char *country1, char *country2, float& result);
// our new service operation: returns currency-converted stock quote
//gsoap ns3 service name: quotex
//gsoap ns3 service style: rpc
//gsoap ns3 service encoding: encoded
//gsoap ns3 schema namespace: urn:quotex
int ns3__getQuote(char *symbol, char *country, float& result);

We run:

soapcpp2 quotex.h

This generates soapStub.h, soapH.h, soapC.cpp (serializers), soapClient.cpp (client stub functions), soapServer.cpp (server skeleton functions).

The quotex.cpp CGI service application is (for source code to create a stand-alone service, see Section How to create a stand-alone server):

// Contents of file "quotex.cpp":
#include "soapH.h"
#include "ns1.nsmap"
int main()
{
struct soap soap;
soap_init(&soap);
soap_serve(&soap);
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
}
int ns3__getQuote(struct soap *soap, char *symbol, char *country, float& result)
{
struct soap tsoap;
float q, r;
soap_init(&tsoap);
if (soap_call_ns1__getQuote(tsoap, "http://services.xmethods.net/soap", "", symbol, &q)
|| soap_call_ns2__getRate(tsoap, "http://services.xmethods.net/soap", NULL, "us", country, &r)
{
soap_delegate_deletion(&tsoap, &soap); // move tsoap-deserialized data to the soap context
if (tsoap->fault)
{
soap->fault = tsoap->fault; // if one of the calls returned a SOAP Fault, we use it
soap_done(&tsoap);
return SOAP_FAULT;
}
soap_done(&tsoap);
return soap_receiver_fault(soap, "Cannot access services", NULL);
}
result = q * r;
soap_delegate_deletion(&tsoap, &soap); // move tsoap-deserialized data to the soap context
soap_done(&tsoap);
return SOAP_OK;
}
/* Since this app is a combined client-server, it is put together with
* one header file that describes all service operations. 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)
{
// dummy: will never be called
}
int ns2__getRate(struct soap *soap, char *country1, char *country2, float &result)
{
// 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 }
};

When combining clients and service functionalities, it is recommended to use a single interface header file input to the soapcpp2 tool, since this header file declares both client and server functionalities. As a consequence, however, stub and skeleton functions are generated for all service operations, while the client part will only use the stub functions and the service part will use the skeleton functions. Thus, dummy implementations of the unused service operations are implemented as shown in the example above, which are in fact never used.

Three WSDL files are generated by soapcpp2: 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.

🔝 Back to table of contents

How to switch to REST from SOAP

To switch to RESTful Web APIs from SOAP Web services APIs is simple, just use a directive.

To declare HTTP POST as the default HTTP method to use with client-side calls for all service operations associated with the ns namespace prefix:

//gsoap ns service protocol: POST

To declare the HTTP POST method for a specific service operation, use:

//gsoap ns service protocol: POST
int ns__webmethod(...);

You can specify GET, PUT, POST, and DELETE. With GET the input parameters of the service operations should be primitive types. See Section Service directives.

🔝 Back to table of contents

Asynchronous one-way message passing

SOAP messaging is typically synchronous: the client sends a HTTP POST and blocks until the server responds to the request. The gSOAP tools also support asynchronous one-way messaging over HTTP.

One-way SOAP service operations are declared as function prototypes with the output parameter specified as a void type to indicate the absence of a return value, for example:

int ns__event(int eventNo, void);

The soapcpp2 tool generates the following functions in soapClient.cpp:

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_send_ns__event function transmits the message to the destination URL by opening a socket and sending the SOAP encoded message. The socket will remain open after the send. To complete the HTTP POST operation we need to call soap_recv_empty_response to accept the server's HTTP OK or Accept response message that should have an empty message body:

if (soap_send_ns__event(soap, eventNo) || soap_recv_empty_response(soap)
soap_print_fault(soap, stderr);

The generated soap_recv_ns__event function can be used to parse a SOAP message, e.g. on the server side. But it is not used on the client side. The ns__event structure is declared as:

struct ns__event
{
int eventNo;
}

The gSOAP generated soapServer.cpp code includes a skeleton function called by soap_serve to process the one-way request message:

int soap_serve_ns__event(struct soap *soap);

This skeleton function calls the user-defined ns__event(struct soap *soap, int eventNo) function (note the absence of the void parameter!). However, when this function returns, the skeleton function does not respond with a SOAP response message since no response data is specified. Instead, the user-defined ns__event function should call soap_send_empty_response to return an empty response message. For example:

int ns__event(struct soap *soap, int eventNo)
{
... // handle event
return soap_send_empty_response(soap, 202); // HTTP 202 Accepted
}

🔝 Back to table of contents

How to use XML serializers to save and load application data

The gSOAP XML databindings for C and C++ allow a seamless integration of XML in C and C++ applications. Data can be serialized in XML and vice versa. WSDL and XML schema files can be converted to C or C++ definitions. C and C++ definitions can be translated to WSDL and schemas to support legacy ANSI C applications for example.

This section explains the basics of mapping XML schema types to C/C++ types using the wsdl2h tool. A more in-depth presentation of C/C++ XML data bindings in gSOAP is documented in C and C++ XML data bindings.

🔝 Back to table of contents

Converting WSDL, WADL, and XML schema to C/C++ with wsdl2h

The wsdl2h tool takes WSDL, WADL, and XSD files or URLs to WSDL, WADL, and XSD and generates an interface header file with the command:

 wsdl2h [options] WSDL WADL and XSD files or URLs...

The WSDL 1.1 and 2.0 standards are supported and WADL. If you have any trouble with wsdl2h being able to process WSDLs and XSD files or URLs, then please contact Genivia technical support for assistance.

The gSOAP tools support the entire XML schema 1.1 standard, except XPath expressions and assertions. This covers all of the following schema components with their optional attributes shown:

1 <xsd:any minOccurs maxOccurs>
2 <xsd:anyAttribute>
3 <xsd:all>
4 <xsd:choice minOccurs maxOccurs>
5 <xsd:sequence minOccurs maxOccurs>
6 <xsd:group name ref>
7 <xsd:attributeGroup name ref>
8 <xsd:attribute name ref type use default fixed form wsdl:arrayType>
9 <xsd:element name ref type default fixed form nillable abstract substitutionGroup minOccurs maxOccurs>
10 <xsd:simpleType name>
11 <xsd:complexType name abstract mixed>

The supported schema facets are:

1 <xsd:enumeration> maps to enum
2 <xsd:simpleContent> maps to primitive type or a typedef
3 <xsd:complexContent> maps to a struct or class
4 <xsd:list> maps to enum* (bitmask)
5 <xsd:extension> maps to extended struct or class with a base class
6 <xsd:restriction> maps to typedef, struct or class
7 <xsd:length> validates string lengths
8 <xsd:minLength> validates string lengths
9 <xsd:maxLength> validates string lengths
10 <xsd:minInclusive> validates integer and float types
11 <xsd:maxInclusive> validates integer and float types
12 <xsd:minExclusive> validates integer and float types
13 <xsd:maxExclusive> validates integer and float types
14 <xsd:precision> float with formatted output
15 <xsd:scale> float with formatted output
16 <xsd:totalDigits> float with formatted output
17 <xsd:fractionDigits> float with formatted output
18 <xsd:pattern> regex pattern, not automatically validated, see note below
19 <xsd:union> maps to string, content not validated

Also supported are:

1 <xsd:import>
2 <xsd:include>
3 <xsd:redefine>
4 <xsd:override>
5 <xsd:annotation>

A subset of the default type mappings is shown below:

1 xsd:string maps to string (char* or std::string)
2 xsd:boolean maps to bool (C++) or enum xsd__boolean (C)
3 xsd:float maps to float
4 xsd:double maps to double
5 xsd:decimal maps to string, or use #import "custom/float128.h"
6 xsd:duration maps to string, or use #import "custom/duration.h"
7 xsd:dateTime maps to time_t, or use #import "custom/struct_tm.h"
8 xsd:time maps to string (white space collapse applied)
9 xsd:date maps to string (white space collapse applied)
10 xsd:gYearMonth maps to string (white space collapse applied)
11 xsd:gYear maps to string (white space collapse applied)
12 xsd:gMonth maps to string (white space collapse applied)
13 xsd:hexBinary maps to struct xsd__hexBinary
14 xsd:base64Binary maps to struct xsd__base64Binary
15 xsd:anyURI maps to string (white space collapse applied)
16 xsd:anyType maps to an XML string or DOM with wsdl2h -d
17 xsd:QName maps to _QName (QName normalization applied)
18 xsd:NOTATION maps to string (white space collapse applied)

Automatic validation of xsd:pattern-restricted content is possible with a hook to a regex pattern matching engine, see the soap::fsvalidate and soap::fwvalidate callback documentation in Section Function callbacks for customized I/O and HTTP handling .

User-defined mappings can be added to typemap.dat, which is used by wsdl2h to map schema types to C/C++ types. For example, the map xsd:duration to a custom serializer, add this line to typemap.dat:

xsd__duration = #import "custom/duration.h" | xsd__duration

Then run wsdl2h with the typemap.dat file in the current directory or use wsdl2h -t mapfile.dat option -t mapfile.dat to use mapfile.dat instead. This requires compiling gsoap/custom/duration.c with your build.

Another example is xsd:dateTime which is mapped to time_t. To expand the range and precision of xsd:dateTime we can map xsd:dateTime to struct tm:

xsd__dateTime = #import "custom/struct_tm.h" | xsd__dateTime

or to struct timeval:

xsd__dateTime = #import "custom/struct_timeval.h" | xsd__dateTime

This requires compiling gsoap/custom/struct_tm.c or gsoap/custom/struct_timeval.c, respectively.

Non-primitive XSD types are supported, with the default mapping shown below:

1 xsd:normalizedString maps to string
2 xsd:token maps to string
3 xsd:language maps to string
4 xsd:IDREFS maps to string
5 xsd:ENTITIES maps to string
6 xsd:NMTOKEN maps to string
7 xsd:NMTOKENS maps to string
8 xsd:Name maps to string
9 xsd:NCName maps to string
10 xsd:ID maps to string
11 xsd:IDREF maps to string
12 xsd:ENTITY maps to string
13 xsd:integer maps to string
14 xsd:nonPositiveInteger maps to string
15 xsd:negativeInteger maps to string
16 xsd:long maps to LONG64
17 xsd:int maps to int
18 xsd:short maps to short
19 xsd:byte maps to byte
20 xsd:nonNegativeInteger maps to string
21 xsd:unsignedLong maps to ULONG64
22 xsd:unsignedInt maps to unsigned int
23 xsd:unsignedShort maps to unsigned short
24 xsd:unsignedByte maps to unsigned byte
25 xsd:positiveInteger maps to string
26 xsd:yearMonthDuration maps to string
27 xsd:dayTimeDuration maps to string
28 xsd:dateTimeStamp maps to string

String targets are defined in the typemap.dat file used by wsdl2h to map XSD types. This allows the use of char* and std::string. It is possible to map any string types to wchar_t and std::wstring by adding the following line to typemap.dat:

xsd__string = | wchar_t* | wchar_t*

and

xsd__string = | std::wstring

By default strings are either char* (for C) or std::string (for C++) which contain ASCII or UTF-8 content enabled with the runtime flag SOAP_C_UTFSTRING.

Note that the XSD types for unlimited numeric values such as xsd:integer and xsd:decimal are mapped to strings, to preserve the value in case it is too large to store in a 64-bit integer or float. The mapping can be redefined as follows in typemap.dat:

xsd__decimal            = | double
xsd__integer            = | LONG64
xsd__nonNegativeInteger = typedef xsd__integer xsd__nonNegativeInteger 0 :   ; | xsd__nonNegativeInteger
xsd__nonPositiveInteger = typedef xsd__integer xsd__nonPositiveInteger   : 0 ; | xsd__nonPositiveInteger
xsd__positiveInteger    = typedef xsd__integer xsd__positiveInteger    1 :   ; | xsd__positiveInteger
xsd__negativeInteger    = typedef xsd__integer xsd__negativeInteger      : -1; | xsd__negativeInteger

We can also use a quadmath.h 128 bit float to store xsd:decimal:

xsd__decimal = #import "custom/float128.h" | xsd__decimal

where xsd__decimal is a __float128 quadmath.h type.

There are several initialization flags to control XML serialization at run-time:

Strict validation catches all structural XML validation violations. For primitive type values it depends on the C/C++ target type that XSD types are mapped to, to catch primitive value content pattern violations. Primitive value content validation is performed on non-string types such as numerical and time values. String values are not automatically validated, unless a xsd:pattern is given and the soap::fsvalidate and soap::fwvalidate callbacks are implemented by the user. Alternatively, deserialized string content can be checked at the application level.

To obtain C or C++ type definitions for XML schema components, run wsdl2h on the schemas to generate a data binding interface header file. This header file defines the C/C++ type representations of the XML schema components. The header file is then processed by the soapcpp2 tool to generate the serializers for these types. See Section Introduction to XML data bindings for an overview to use wsdl2h and soapcpp2 to map schemas to C/C++ types to obtain XML data bindings.

🔝 Back to table of contents

Mapping C/C++ to XML schema with soapcpp2

To generate serialization code, execute:

 soapcpp2 [options] file.h

The following C/C++ types are supported in the data binding interface header file:

bool
enum, enum * (enum * is a "product enumeration" representing a bitmask)
(signed or unsigned) char, int8_t, short, int16_t, int, int32_t, long, long long, int64_t, LONG64
size_t (transient, not serializable)
float, double, long double (#import "custom/long_double.h")
std::string, std::wstring, char[], char*, wchar_t*
_XML (a char* type to hold literal XML string content)
_QName (a char* type with normalized QName content of the form prefix:name)
struct, class (with single inheritance)
std::vector, std::list, std::deque, std::set
union (requires preceding discriminant member)
typedef
time_t
template <> class (containers require begin(), end(), size(), and insert() methods)
void* (requires a preceding __type member to indicate the object pointed to)
struct xsd__hexBinary (special pre-defined type to hold binary content)
struct xsd__base64Binary (special pre-defined type to hold binary content)
struct tm (#import "custom/struct_tm.h")
struct timeval (#import "custom/struct_timeval.h")
pointers to any of the above (any pointer-linked structures are serializable, including cyclic graphs)
std::shared_ptr, std::unique_ptr, std::auto_ptr
fixed-size arrays of all of the above

Additional features and C/C++ syntax requirements:

  • A header file should not include any code statements, only data type declarations.
  • Nested structs, classes, and unions are un-nested.
  • Use #import "file.h" instead of #include to import other header files. The #include and #define directives are fine to use, but these are moved into the generated code and then used by the C/C++ compiler.
  • C++ namespaces are supported, but must cover the entire header file content.
  • Optional DOM support can be used to store mixed content or literal XML content can be stored in _XML strings. Otherwise, mixed content may be lost. Use soapcpp2 -d option -d for DOM support. See the XML DOM API documentation for details.
  • Types are denoted transient using the extern qualifier, which prevents serialization of types or struct and class members:
    extern class classname; // this class is not serializable
    struct structname
    {
    extern char *name; // this member is not serializable
    int num;
    };
  • Only public members of a class can be serialized:
    class name
    { private:
    char *secret; // private and protected members are not serializable
    };
    and members are public by default in the interface header file for soapcpp2.
  • Types may be declared volatile which means that they are declared elsewhere in the project's source code base and should not be redefined in the soapcpp2-generated code nor changed by the soapcpp2 tool, for example this makes struct tm of time.h serializable with a selection of its members specified, where volatile prevents soapcpp2 from declaring this struct again:
    volatile struct tm
    {
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    };
  • Classes and structs may be declared mutable means that they can be augmented with additional members using redefinitions of the struct or class:
    mutable class classname
    { public:
    int n; // classname has a member 'n'
    };
    mutable class name
    { public:
    float x; // classname also has a member 'x'
    };
    The SOAP_ENV__Header struct is mutable as well as the SOAP_ENV__Fault, SOAP_ENV__Detail, SOAP_ENV__Reason, and SOAP_ENV__Code structs. The reason is that these structures are augmented with additional members by plugins such as WS-Addressing gsoap/plugin/wsaapi.h to support these SOAP-based protocols.
  • Members of a struct or class are serialized as XML attributes when qualified with '@', for example:
    struct record
    {
    @ char *name; // XML attribute name
    int num; // XML element num
    };
  • Strings with 8-bit content hold ASCII by default or hold UTF-8 when enabled with runtime flag SOAP_C_UTFSTRING. When enabled, all std::string and char* strings contain UTF-8. In this way the deserializers populate strings with UTF-8 content and serializers will output strings as holding UTF-8 content.

The soapcpp2 tool generates serializers and deserializers for all wsdl2h-generated or user-defined data structures that are specified in the header file input to the soapcpp2 tool. The serializers and deserializers can be found in the soapcpp2-generated soapC.cpp file. These serializers and deserializers can be used separately by an application without the need to build a Web services 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.

🔝 Back to table of contents

Serializing C/C++ data to XML

The soapcpp2 tool generates the following readers and writers for each serializable data type defined in the data bindings interface file input to soapcpp2:

  • int soap_read_T(struct soap*, T *data) parse XML and deserialize into C/C++ data of type T, returns SOAP_OK on success.
  • int soap_write_T(struct soap* T *data) serialize C/C++ data of type T into XML, returns SOAP_OK on success.

Where T is the name of the data type, such as the struct or class name. For other types, see the table further below for the naming conventions used by soapcpp2 to generate these functions.

The following soap context variables control the destination and source for XML serialization and deserialization:

  • SOAP_SOCKET soap::socket socket file descriptor for socket connection input and output (or SOAP_INVALID_SOCKET when not set).
  • ostream *soap::os C++ only: output stream used for send operations when non-NULL.
  • const char **soap::os C only: points to a string pointer to be set with the string content produced, the saved string is allocated and managed by the soap context.
  • istream *soap::is C++ only: input stream used for receive operations when non-NULL.
  • const char *soap::is C only: string with input to parse, this pointer advances over the string until a \0 is found.
  • int soap::sendfd when soap::socket = SOAP_INVALID_SOCKET, this fd is used for send operations, default fd is 1 (stdout).
  • int soap::recvfd when soap::socket = SOAP_INVALID_SOCKET, this fd is used for receive operations, default fd is 0 (stdin).

Additional functions are generated by soapcpp2 for each serializable data type T to dynamically allocate data of type T on the context-managed heap and to initialize data of type T:

  • T * soap_new_T(struct soap*) allocates and initializes data of type T in context-managed heap memory, managed data is deleted with soap_destroy (deletes C++ objects) and soap_end (deletes all other data), and you can also use soap_malloc to allocate uninitialized context-managed memory.
  • void soap_default_T(struct soap*, T*) initializes data of type T, but C++ classes are augmented with a soap_default(struct soap*) method that should be called instead to (re)initialize the class instance. If the class has a soap context pointer member then this member will be set to the first argument passed to this function.

The following extra functions are generated by soapcpp2 for deep copying and deletion of entire data structures when using soapcpp2 -Ecd options -Ec (deep copy) and -Ed (deep deletion):

  • T * soap_dup_T(struct soap*, T *dst, const T *src) deep copy src into dst, replicating all deep cycles and shared pointers when a managing soap context is provided. When dst is NULL, allocates space for dst and returns a pointer to the allocated copy. Deep copy results in a tree when the soap context is NULL, but the presence of deep cycles will lead to non-termination. Use flag SOAP_XML_TREE with managing context to copy into a tree without cycles and pointers to shared objects. Returns dst or the allocated copy when dst is NULL.
  • void soap_del_T(const T*) deletes all heap-allocated members of this object by deep deletion ONLY IF this object and all of its (deep) members are not managed by a soap context AND the deep structure is a tree (no cycles and co-referenced objects by way of multiple (non-smart) pointers pointing to the same data). Can be safely used after soap_dup(NULL) to delete the deep copy. Does not delete the object itself.

The following initializing and finalizing functions should be used before and after calling lower-level IO functions such as soap_send, soap_send_raw, soap_get0, soap_get1, and soap_http_get_body (this is not needed when calling the soap_read_T and soap_write_T functions):

These operations do not setup or open or close files or connections. The application should open and close connections or files and set the soap::socket, soap::os or soap::sendfd, soap::is or soap::recvfd streams or descriptors. When soap::socket is SOAP_INVALID_SOCKET and none of the streams and descriptors are set, then the standard input and output will be used.

The following options are available to control serialization:

soap_set_mode(soap, SOAP_XML_TREE); // use this for XML without id-ref (no cycles!)
soap_set_mode(soap, SOAP_XML_GRAPH); // or use this for XML with id-ref (including cycles)
soap_set_namespaces(soap, struct Namespace *nsmap); // set a XML namespace table with xmlns bindings

See also Section Run-time flags to control the I/O buffering and content encoding such as compression.

To accurately and safely serialize data structures with cycles and co-referenced objects to an XML stream, two generated functions are called by soap_write_T to serialize data of type T: soap_serialize_T to perform a deep analysis of pointers to detect co-referenced data and cycles, and soap_put_T to output the data in XML with id-href (or id-ref) attributes for co-referenced data and cycles. Multi-references with id-href (and id-ref) are part of the SOAP protocol to serialize data accurately, i.e. retaining the structural integrity of the data sent and received. Flag SOAP_XML_TREE turns id-href (id-ref) attributes off (makes soap_serialize_T a no-op and ignores them on the receiving end). Flag SOAP_XML_GRAPH should be used with non-SOAP XML output to accurately and safely serialize data structure graphs with co-referenced objects and cycles.

The soap_serialize_T and soap_put_T calls are performed by the generated soap_write_T functions, which also call soap_begin_send and soap_end_send.

The following table lists the type naming conventions used by soapcpp2 to generate functions:

type name
char* string
wchar_t* wstring
std::string std__string
std::wstring std__wstring
char byte
bool bool
double double
int int
float float
long long
long long LONG64
short short
time_t time
unsigned char unsignedByte
unsigned int unsignedInt
unsigned long unsignedLong
unsigned long long ULONG64
unsigned short unsignedShort
T[N] ArrayNOfType where Type is the type name of T
T* PointerToType where Type is the type name of T
std::vector<T> TemplateOfType where Type is the type name of T
struct Name Name
class Name Name
enum Name Name

Consider for example the following interface header file for soapcpp2 declares a struct ns__Person:

struct ns__Person
{
char *name;
};

To parse and deserialize a person variable p from XML:

struct soap *soap = soap_new();
struct ns__Person *p = soap_new_ns__Person(soap, -1); // -1 is one (non-array)
soap->recvfd = 0; // parse from stdout
// soap->is = &std::in; // parse from stdin stream (C++ only)
// soap->is = cs; // parse from a char *cs string (C only)
if (soap_read_ns__Person(soap, p))
soap_print_fault(soap, stderr);
... // use value p
soap_end(soap);
soap_free(soap);

To parse and deserialize XML from a file:

soap->recvfd = open(file, O_RDONLY);
if (soap->recvfd >= 0)
if (soap_read_ns__Person(soap, p))
soap_print_fault(soap, stderr);
close(soap->recvfd);
soap->recvfd = 0;

To parse and deserialize XML from a C++ file stream:

std::fstream fs;
fs.open(file, std::ios::in);
if (fs)
{
soap->is = &fs;
if (soap_read_ns__Person(soap, p))
soap_print_fault(soap, stderr);
fd.close();
soap->is = NULL;
}

Or to parse and deserialize XML from a string stream in C++:

std::stringstream ss;
... // populate stream ss
soap->is = &ss;
if (soap_read_ns__Person(soap, p))
soap_print_fault(soap, stderr);
soap->is = NULL;

To parse and deserialize XML from a string cs in C:

const char *cs = "..."; // populate string cs
soap->is = &cs;
if (soap_read_ns__Person(soap, p))
soap_print_fault(soap, stderr);
soap->is = NULL;

To serialize a person variable p in XML:

struct soap *soap = soap_new1(SOAP_XML_INDENT);
struct ns__Person *p = soap_new_ns__Person(soap, -1); // -1 is one (non-array)
p->name = "Joe";
soap->sendfd = 1; // send to stdout
// soap->os = &std::cout; // send to stdout stream (C++ only)
// soap->os = &cs; // send to a char *cs string (C only)
if (soap_write_ns__Person(soap, p))
soap_print_fault(soap, stderr);
soap_end(soap);
soap_free(soap);

This produces:

1 <ns:Person xmlns:ns="..." ... >
2  <name>Joe</name>
3 </ns:Person>

To send the output to a file:

soap->sendfd = open(file, O_RDWR|O_CREAT, S_IWUSR|S_IRUSR);
if (soap->sendfd >= 0)
if (soap_write_ns__Person(soap, p))
soap_print_fault(soap, stderr);
close(soap->sendfd);
soap->sendfd = 1;

To send the output to a C++ file stream:

std::fstream fs;
fs.open(file, std::ios::out);
if (fs)
{
soap->os = &fs;
if (soap_write_ns__Person(soap, p))
soap_print_fault(soap, stderr);
fd.close();
soap->os = NULL;
}

Or send the output to a C++ string stream to save XML in a string:

std::stringstream ss;
soap->os = &ss;
if (soap_write_ns__Person(soap, p))
soap_print_fault(soap, stderr);
std::strings s = ss.str();
soap->os = NULL;

To save the output to a string cs in C:

char *cs;
soap->os = &cs;
if (soap_write_ns__Person(soap, p))
soap_print_fault(soap, stderr);
soap->os = NULL;

The string cs is populated with XML when successful. This string is managed by the context and deleted with soap_end.

As we explained, the soap_write_T functions call soap_serialize_T, which must be called when the data structure graph to serialize contains co-referenced data and cycles. It must be called to preserve the logical coherence of pointer-based data structures, where pointers may refer to co-referenced objects. By calling soap_serialize_T, data structures shared through pointers are serialized only once and referenced in XML using id-refs attributes. The actual id-refs used depend on the SOAP encoding. To turn off SOAP encoding, remove or avoid using the SOAP-ENV and SOAP-ENC namespace bindings in the namespace table. In addition, the SOAP_XML_TREE and SOAP_XML_GRAPH flags can be used to control the output by restricting serialization to XML trees or by enabling multi-ref graph serialization with id-ref attributes.

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. This flag also instructs the XML parser and deserializer to ignore id-ref attributes.

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 Run-time flags ). Using the SOAP_XML_GRAPH flag assures the preservation of the logical structure of the data.

Using SOAP_XML_TREE means that no id-refs are output or parsed. With this flag the output will serialize nodes as a tree in XML, which means that nodes may be duplicated when shared by multiple pointers and cycles are broken to prevent infinite serialization. To preserve the graph structure of the nodes in the data structure, use SOAP_XML_GRAPH or use SOAP 1.1 or 1.2 multi-reference serialization (this is the default mode with SOAP serialization).

Consider for example the following struct:

struct Tricky
{
int *p;
int n;
int *q;
};

The following fragment initializes the pointer members p and q to point to the value of member n:

struct soap *soap = soap_new();
struct Tricky X;
X.n = 123;
X.p = &X.n;
X.q = &X.n;
soap_write_Tricky(soap, &X);
soap_end(soap);

What is special about this data structure is that members p and q both point to member n. When using SOAP 1.1 with gSOAP, the serializers strategically place id elements (also called SOAP 1.1 independent elements) after the root element to identify shared values, where the href attributes of elements p and q point to:

1 <Tricky>
2  <p href="#_1"/>
3  <n>1</n>
4  <q href="#_1"/>
5 </Tricky>
6 <id id="_1">123</id>

The above is not valid as plain XML, because there is no single root element, but it is valid XML when placed in a SOAP Body element as intended with SOAP 1.1 messaging.

When SOAP 1.2 is used with gSOAP, the output is more accurate, because now both elements p and q point to element n:

1 <Tricky>
2  <p SOAP-ENC:ref="_1"/>
3  <n SOAP-ENC:id="_1">123</n>
4  <q SOAP-ENC:ref="_1"/>
5 </ns:Tricky>

Without using SOAP encoding but using plain XML instead with the SOAP_XML_GRAPH flag set, the output is also accurate with both elements p and q pointing to element n:

1 <Tricky>
2  <p ref="_1"/>
3  <n id="_1">123</n>
4  <q ref="_1"/>
5 </Tricky>

In the last two cases, the generated deserializer for this data type will be able to accurately reconstruct the instance with members p and q pointing to member n.

Finally, serialization with SOAP_XML_TREE produces XML trees, which may benefit interoperability but sacrifices the true meaning of serialization, giving three copies of the shared value 1:

1 <Tricky>
2  <p>123</p>
3  <n>123</n>
4  <q>123</q>
5 </Tricky>

With the soapcpp2-generated serializers you can define a C++ operator that serializes a specified class instance or type as follows, assuming the ns__Person class is declared in an interface header file for soapcpp2:

class ns__Person
{ public:
ns__Person();
~ns__Person();
void set_name(const char *);
const char *get_name();
const char *name;
struct soap *soap;
};

Run soapcpp2 -0 on this file to generate the serializers with non-SOAP XML namespaces, which we then use in our main program as follows:

#include "soapH.h"
#include "ns.nsmap"
ns__Person::ns__Person()
{
name = NULL;
soap = soap_new1(SOAP_XML_INDENT | SOAP_XML_TREE); // or SOAP_XML_GRAPH
}
ns__Person::~ns__Person()
{
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
}
void ns__Person::set_name(const char *name)
{
this->name = soap_strdup(this->soap, name);
}
const char *ns__Person::get_name()
{
return this->name;
}
std::ostream& operator<<(std::ostream& o, ns__Person& p)
{
p.soap->os = &o;
soap_write_ns__Person(p.soap, &p);
p.soap->os = NULL;
return o;
}
std::istream& operator>>(std::istream& i, ns__Person& p)
{
p.soap->is = &i;
soap_read_ns__Person(p.soap, &p);
p.soap->is = NULL;
return i;
}
int main()
{
ns__Person p;
p.set_name("Joe");
// serialize person p in XML to stdout:
std::cout << p << std::endl;
// then parse and deserialize XML from stdin into person p:
std::cin >> p;
// destructor cleans up person p and its deserialized data
}

In this example we construct an instance of ns__Person by setting its soap context struct pointer data member to a new valid context that is deleted by the destructor of this instance.

Warning
Deserialized class instances with a soap context struct pointer member will have their soap contexts set automatically by the deserializer's context, because soap_default_T (or soap_default class method) is called that sets the soap context struct pointer of the instance. For example, soap_read_ns__Person sets the deserialized ns__Person::soap member to the first argument soap of soap_read_ns__Person, which happens to be ns__Person::soap anyway in the example shown above. See Section Intra-class memory management .

The output of this program is:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <ns:Person
3  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
5  xmlns:ns="http://tempuri.org/ns.xsd">
6  <name>Joe</name>
7 </ns:Person>

The xsi and xsd namespaces are used when attributes such as xsi:nil and xsi:type are serialized, where xsi:type may refer to a XSD type such as xsd:string. The xsi:nil attribute is output when an element is nillable but its corresponding pointer member is NULL and xsi:type is output for structs and classes with polymorphic members that are declared with int __type member and a void* pointer to serialize the value pointed to. Attribute xsi:type is also output to serialize derived instances in place of base class instances. Therefore, removing these namespaces from the XML namespace table (ns.nsmap) may cause XML parsing and validation issues.

🔝 Back to table of contents

Example

As an example, consider the following data type declarations:

// Contents of file "person.h":
enum ns__Gender { male, female };
class ns__Address
{ public:
const char *street;
uint32_t number;
const char *city;
};
class ns__Person
{ public:
const char *name;
enum ns__Gender gender;
ns__Address address;
ns__Person *mother;
ns__Person *father;
};

The following program uses these data types to write to standard output a data structure that contains the data of a person named "John" living at Downing st. 10 in Londen. He has a mother "Mary" and a father "Stuart". After initialization, the class instance for "John" is serialized and encoded in XML to the standard output stream using gzip compression (requires the Zlib library, compile sources with the compile-time flag WITH_GZIP):

// Contents of file "person.cpp":
#include "soapH.h"
#include "ns.nsmap"
int main()
{
struct soap *soap = soap_new1(SOAP_XML_GRAPH);
ns__Person mother, father, john;
mother.soap_default(soap);
father.soap_default(soap);
john.soap_default(soap);
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_write_ns__Person(soap, &john);
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
}

The person.h interface header file is input to soapcpp2 and the generated code compiled together with person.cpp:

 soapcpp2 -0 person.h
 c++ -o person person.cpp soapC.cpp stdsoap2.cpp

We run the application:

 ./person

The output is:

1 <ns:Person
2  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
4  xmlns:ns="urn:person">
5  <name>John</name>
6  <gender>male</gender>
7  <address>
8  <street id="_3">Dowling st.</street>
9  <number>10</number>
10  <city id="_4">London</city>
11  </address>
12  <mother>
13  <name>Mary</name>
14  <gender>female</gender>
15  <address>
16  <street ref="_3"/>
17  <number>5</number>
18  <city ref="_4"/>
19  </address>
20  </mother>
21  <father>
22  <name>Stuart</name>
23  <gender>male</gender>
24  <address>
25  <street>Main st.</street>
26  <number>13</number>
27  <city ref="_4"/>
28  </address>
29  </father>
30 </ns:Person>

Because the C++ compiler stores the constant strings "Dowling st." and "London" just once, references are included in the output with flag SOAP_XML_GRAPH that preserves the original structure of the data structure serialized.

The following program decodes this content from standard input and reconstructs the original data structure on the heap:

#include "soapH.h"
#include "ns.nsmap"
int main()
{
struct soap *soap = soap_new();
ns__Person john;
if (soap_read_ns__Person(soap, &john))
{
soap_print_fault(soap, stderr);
}
else
{
ns__Person *mother = john->mother;
ns__Person *father = john->father;
... // use the data
}
soap_destroy(soap); // deletes john, mother and father
soap_end(soap); // deletes other managed data and temporaries
soap_free(soap); // finalize and delete the context
}

🔝 Back to table of contents

Default values for omitted XML elements and attributes

The soapcpp2 tool generates soap_default_T functions for serializable types T specified in an interface header file for soapcpp2 The default values of primitive C/C++ types can be easily specified by defining any one or all of the following macros before including the gsoap/stdsoap2.h file or by using SOAPDEFS_H or WITH_SOAPDEFS_H:

#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

The absence of a data value in a receiving SOAP message will result in the assignment of a default value to a primitive type upon deserialization.

Default values can also be assigned to individual struct and class members of primitive type or pointers to primitive types. For example:

struct MyRecord
{
char *name = "Unknown"; // optional
int value = 9999;
enum Status { active, passive } status = passive;
}

Default values are assigned to the members of a struct or class when parsing and deserializing XML into data when XML elements or attributes with the respective values are absent. Assigning default values to members makes these members optional elements and attributes in the corresponding XML schema.

Because service operation requests and responses are essentially structs (internally they are structs), default values can also be assigned to service operation parameters. These default parameter values do not specify optional parameters as we normally see with C/C++ function calls. Rather, the default parameter values are used in case an inbound request or response message lacks the XML elements that comprise these parameters. For example, a Web service can use default values to fill-in absent parameters in a SOAP request as follows:

int ns__login(char *uid = "anonymous", char *pwd = "guest", bool granted = true);

When the request message lacks uid or pwd elements then the default values are assigned instead.

In addition, the default values will show up in the SOAP or XML request and response message examples generated by the soapcpp2 tool.

🔝 Back to table of contents

The wsdl2h tool

The wsdl2h tool is an advanced XML data binding tool to convert WSDLs and XML schemas (XSD files) to C or C++. The tool takes WSDL and XSD files or URLs to WSDLs and XSDs, then converts these to a C or C++ interface header file that specifies the properties of the WSDLs and XSDs in a familiar C/C++ syntax. This header file is not intended to be included in your code directly. It should be converted by soapcpp2 to generate the logic for the data bindings. It can however be safely converted by a documentation tool such as Doxygen to analyze and represent the service operations and data in a convenient layout. To this end, the generated interface header file is self-explanatory.

The wsdl2h tool can also be used without WSDLs to convert XML schemas (XSDs) to C/C++ to implement XML data bindings in C and C++. The wsdl2h tool generates the XML data binding interface header file with the C/C++ data type equivalents to the XML schema types and components.

The soapcpp2 tool then generates the XML data binding implementation source code from the data binding interface header file, meaning the serialization source code to serialize C/C++ data in XML and the client-side stub functions to invoke remote service operations and the server-side skeleton functions to implement XML Web services.

Therefore, the creation of C and C++ applications from one of more WSDLs or XSDs is a two-step process.

First, to convert a WSDL to C++ we use:

 wsdl2h file.wsdl

This generates an interface header file file.h. When using a URL to the WSDL we use wsdl2h -o file.h option -o file.h to save the file:

 wsdl2h -ofile.h http://www.example.com/file.wsdl

Web service operations in the generated file.h header file are converted to function prototypes. Schema types are converted to the equivalent C/C++ types, using file typemap.dat to map XML schema types to C/C++ types.

The generated header file also contains instructions for the user and has documentation copies from the WSDL as well as various directives related to the Web service properties defined in the WSDL.

Multiple WSDL specifications can be processed at once and saved to one interface header file with wsdl2h -o file.h option -o file.h:

 wsdl2h -o file.h file1.wsdl file2.wsdl file3.wsdl

To generate C source code, use wsdl2h -c option -c:

 wsdl2h -c file.wsdl

The wsdl2h tool does not require WSDLs, it also works for XSDs:

 wsdl2h -o file.h file1.xsd file2.xsd file3.xsd

In this case no service operations are found and therefore the interface header file generated does not contain function prototypes representing service operations.

When upgrading gSOAP to a newer version it is often not necessary to perform this first step again, since newer versions are backward compatible to previous interface header files generated by wsdl2h.

Next, the wsdl2h-generated interface header file file.h is input to the soapcpp2 tool to generate the XML data binding implementation logic in C or C++:

 soapcpp2 file.h

You can use soapcpp2 without wsdl2h, by specifying an input interface header file that is not generated by wsdl2h but written by hand for example, see The soapcpp2 tool.

There are many cases when wsdl2h generates code with #import directives, such as #import "stlvector.h", that requires the soapcpp2 tool to import definitions from the gsoap/import directory, which can be specified as follows:

 soapcpp2 -I some_path_to/gsoap/import file.h

When WSDLs are converted to C++ source code, you may want to use wsdl2h -j option -j (or wsdl2h -j option -i) to generate proxy and service classes:

 soapcpp2 -j file.h

This command generates a couple of C++ source files, more details will follow in Section The soapcpp2 tool.

Consider for example the following commands to implement a C++ client application:

 wsdl2h -o calc.h http://www.genivia.com/calc.wsdl 
 soapcpp2 -C -j -I path_to/gsoap/import calc.h

The first command generates calc.h from the WSDL at the specified URL. The header file is then processed by the soapcpp2 tool to generate the proxy class declared in soapcalcProxy.h class and defined in soapcalcProxy.cpp. It also generates a file calc.nsmap with a XML namespace table which should be included in our source code. The tool also generates soapStub, soapH.h, and soapC.cpp. The latter is also compiled with our application together with gsoap/stdsoap2.cpp.

🔝 Back to table of contents

wsdl2h options

The wsdl2h tool generates one XML data binding interface header file, a file that includes all of the information gathered from the WSDLs and XSDs input to the wsdl tool as command-line arguments. The default output file name of wsdl2h is the first WSDL/schema input file name but with extension .h that replaces .wsdl (or replaces .xsd in case of XSD files specified). When an input file is absent or a WSDL file is loaded from a Web URL, the header output will be produced on the standard output unless wsdl2h -o file.h option -o file.h is used to save the output to file.h (or any other file name specified).

The wsdl2h command-line options are:

option result
-a generate indexed struct names for local elements with anonymous types
-b generate bi-directional operations to serve one-way response messages (duplex)
-c generate C source code
-c++ generate C++ source code (default)
-c++11 generate C++11 source code
-D make attribute members with default/fixed values optional with pointers
-d generate DOM code for xsd:any and xsd:anyType elements
-e don't qualify enum names
-F add transient members to structs to simulate struct-type derivation in C
-f generate flat C++ class hierarchy by removing inheritance
-g generate global top-level element and attribute declarations
-h display help info and exit
-I path use path to locate WSDL and XSD files
-i don't import (advanced option)
-j don't generate SOAP_ENV__Header and SOAP_ENV__Detail definitions
-k don't generate SOAP_ENV__Header mustUnderstand qualifiers
-L generate less documentation by removing generic @note comments
-l display license information
-M suppress error "must understand element with wsdl:required='true'"
-m use xsd.h module to import primitive types
-N name use name for service prefixes to produce a service for each binding
-n name use name as the base namespace prefix name instead of ns
-O1 optimize by omitting duplicate choice/sequence members
-O2 optimize -O1 and omit unused schema types (unreachable from roots)
-O3 optimize -O2 and omit unused schema root attributes
-O4 optimize -O3 and omit unused schema root elements (use only with WSDLs)
-Ow2 optimize -O2 while retaining all derived types of used base types
-Ow3 optimize -O3 while retaining all derived types of used base types
-Ow4 optimize -O4 while retaining all derived types of used base types
-o file output to file
-P don't create polymorphic types inherited from xsd__anyType
-p create polymorphic types inherited from base xsd__anyType (automatic when the WSDL or XSD contains polymorphic definitions)
-Q make xsd__anySimpleType equal to xsd__anyType to use as the base type
-q name use name for the C++ namespace of all declarations
-R generate REST operations for REST bindings in the WSDL
-r host[:port[:uid:pwd]] connect via proxy host, port, and proxy credentials uid and pwd
-r :uid:pwd connect with authentication credentials uid and pwd
-S name use name instead of soap for the soap context included in C++ classes as a member variable or use -S "" to remove it
-s don't generate std code (no std::string and no std::vector)
-t file use type map file instead of the default file typemap.dat
-U map Unicode XML names to UTF-8-encoded Unicode C/C++ identifiers
-u don't generate unions
-V display the current version and exit
-v verbose output
-W suppress warnings
-w always wrap response parameters in a response struct
-X don't qualify part names to disambiguate doc/lit wrapped patterns
-x don't generate _XML any and _XML anyAttribute extensibility elements
-y generate typedef synonyms for structs and enums
-z1 compatibility with 2.7.6e: generate pointer-based arrays
-z2 compatibility with 2.7.15: (un)qualify element/attribute referenced members
-z3 compatibility with 2.7.16 to 2.8.7: (un)qualify element/attribute referenced members
-z4 compatibility up to 2.8.11: don't generate union structs in std::vector
-z5 compatibility up to 2.8.15: don't include minor improvements
-z6 compatibility up to 2.8.17: don't include minor improvements
-z7 compatibility up to 2.8.59: don't generate std::vector of class of union
-z8 compatibility up to 2.8.74: don't generate qualifiers for doc/lit wrapped patterns
-z9 compatibility up to 2.8.93: always qualify element/attribute referenced members, even when defined in the same namespace with default forms unqualified
-z10 compatibility up to 2.8.96: generate qualifiers even when defined without namespace
-_ don't generate _USCORE (replace with Unicode _x005f)

The following subsections explain the options in detail. The source code examples generated by wsdl2h are slightly simplified by removing comments and some other details without changing their meaning to improve readability.

🔝 Back to table of contents

wsdl2h -a

This option generates indexed identifier names for structs, classes, unions, and enums declared for local elements with local (i.e. anonymous) types. When local elements and attributes have local types that are mapped to a struct, class, union, or enum, the generated type name is normally the outer struct/class name concatenated with the element/attribute name.

For example:

1 <xsd:schema targetNamespace="urn:example" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2  <xsd:complexType name="TypeWithNestedType">
3  <xsd:sequence>
4  <xsd:element name="element">
5  <xsd:complexType>
6  <xsd:sequence>
7  <xsd:element name="nested-element" type="xsd::string"/>
8  </xsd:sequence>
9  </xsd:complexType>
10  </xsd:element>
11  </xsd:sequence>
12  </xsd:complexType>
13 </xsd:schema>

By default without this option, this schema is translated by wsdl2h to the following interface header file declaration:

class ns__TypeWithNestedType // complexType
{ public:
class ns__TypeWithNestedType_element // local complexType
{ public:
std::string nested_element; // nested required element
} element; // required element
};

By contrast, with wsdl2h -a option -a we obtain an indexed local class _ns__struct_1 in the generated interface header file for soapcpp2:

class ns__TypeWithNestedType // complexType
{ public:
class _ns__struct_1 // local complexType
{ public:
std::string nested_element; // nested required element
} element; // required element
};

The next local struct or class is named _ns__struct_2 and so on. The same indexing applies to local unions and enums.

Note
The soapcpp2 tool always un-nests nested struct, class, union and enum declarations, which means that the above eventially results in the following source code generated by soapcpp2:
class _ns__struct_1
{ public:
std::string nested_element;
};
class ns__TypeWithNestedType
{ public:
_ns__struct_1 element;
};

🔝 Back to table of contents

wsdl2h -b

This option generates bi-directional operations (duplex operations) intended for asynchrounous server operations. The bi-directional operations for server response messages are generated in addition to the request-response operations.

For example, the wsdl2h tool generates the following declaration of a service operation ns__add for a hypothetical calculator Web service:

int ns__add(
double a,
double b,
double& result
);

By contrast, with this option -b we obtain an additional one-way operation ns__addResponse to send and receive one-way response messages:

int ns__addResponse(
double result,
void
);
int ns__add(
double a,
double b,
double& result
);

Where void as a result parameter means that the operation uses "one-way" messaging, in this case to send and receive response messages one-way asynchronously:

  • int soap_send_ns__addResponse(struct soap *soap, const char *endpoint, const char *action, double& result)
  • int soap_recv_ns__addResponse(struct soap *soap, double& result)

At the sender side use soap_send_ns__addResponse to send the message one-way, followed by soap_recv_empty_response to receive the HTTP acknowledgment. At the receiver side use soap_recv_ns__addResponse. To develop a server, simply implement soap_ns__addResponse to handle the service operation and in this function call soap_send_empty_response to send the HTTP acknowledgment. The same applies to C++ proxy classes generated by soapcpp2.

Note
Version 2.8.75 of gSOAP and greater generate send and receive functions for each client-side call function. This means that a client application can simply call soap_send_ns__add to send the request and then call soap_recv_ns__add to receive the response after polling the server connection with soap_ready to check if the server is ready (soap_ready returns SOAP_OK) to send the response message as a reply message to be received by the client. Therefore, this option -b is not required to implement asynchronous request-response messaging but rather adds one-way asynchronous response messaging as well.

🔝 Back to table of contents

wsdl2h -c -c++ -c++11

This option sets the source code output to C, C++, or C++11, respectively.

For C++ and C++11 you can also use wsdl2h -s option -s to replace std::vector by arrays and replaces std::string by char*. Use a typemap.dat file to specify further details for the source code output generated by wsdl2h.

🔝 Back to table of contents

wsdl2h -D

This option makes attribute members of a struct or class with default or fixed values optional with pointers. Elements with default and fixed values are not affected by this option.

Without this option, optional attributes with default or fixed values are always output in XML, because the struct/class attribute member is not a pointer. This does not negatively affect the meaning of the XML produced, because omitted attributes are replaced by their default or fixed value.

For example:

1 <xsd:schema targetNamespace="urn:example" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2  <xsd:complexType name="data">
3  <xsd:sequence>
4  <xsd:element name="foo" type="xsd:string" minOccurs="0" default="abc"/>
5  </xsd:sequence>
6  <xsd:attribute name="bar" type="xsd:int" use="optional" default="123"/>
7  </xsd:complexType>
8 </xsd:schema>

By default without this option, this schema is translated by wsdl2h to the following interface header file declaration:

class ns__data
{ public:
std::string* foo 0 = "abc"; // optional element with default value "abc"
@ int bar 0 = 123; // optional with default value 123
};

The deserializer populates the attribute value with the default or fixed value when the attribute is omitted from XML. The element is populated when the element is empty, i.e. <bar/> or <bar></bar>, but not when it is omitted, as per the W3C XML Schema standards.

This option forces the optional attributes to be pointer-based members, meaning that their output can be turned on or off by setting the pointer to a value or to NULL:

class ns__data
{ public:
std::string* foo 0 = "abc"; // optional element with default value "abc"
@ int* bar 0 = 123; // optional with default value 123
};
Warning
In this case the deserializer will not populate the attribute value with the default or fixed value when the attribute is omitted from XML, which differs from the W3C XML schema standards.

🔝 Back to table of contents

wsdl2h -d

This option replaces literal XML strings _XML (a char* string with XML content) with DOM nodes that are used to store the content of xsd:any, xsd:anyAttribute xsd:anyType, and mixed content values. The DOM API offers more features to manipulate XML content compared to the literal _XML string type.

The DOM node type xsd__anyType of the gSOAP DOM API is imported in the wsdl2h-generated interface header file with #import "dom.h" where dom.h is located in the gsoap/import directory. This requires compiling gsoap/dom.c in C and gsoap/dom.cpp in C++.

For example:

1 <xsd:schema targetNamespace="urn:example" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2  <xsd:complexType name="data">
3  <xsd:sequence>
4  <xsd:element name="foo" type="xsd:anyType"/>
5  <xsd:any/>
6  </xsd:sequence>
7  <xsd:anyAttribute processContents="lax"/>
8  </xsd:complexType>
9 </xsd:schema>

By default without this option, this schema is translated by wsdl2h to the following interface header file declarations:

class xsd__anyType
{ public:
_XML __item; // XML string content
};
class ns__data : public xsd__anyType
{ public:
xsd__anyType* foo;
_XML __any; // Store any element content in XML string
@ _XML __anyAttribute; // A placeholder that has no effect
};

The xsd__anyType type has _XML simpleContent stored in __item. Names starting with double underscores have no representation in XML as elements or attribute names, meaning that only their values matter. Therefore, _XML __any holds the element and its content in a string.

With wsdl2h -d option -d we obtain:

#import "dom.h" // imports xsd__anyType as a DOM node
class ns__data : public xsd__anyType
{ public:
xsd__anyType* foo; // Store <foo> element in DOM soap_dom_element
xsd__anyType __any; // Store any element content in DOM soap_dom_element
@ xsd__anyAttribute __anyAttribute; // Store anyAttribute content in DOM soap_dom_attribute linked node structure
};

See DOM API for details on how to use the xsd__anyType and xsd__anyAttribute.

🔝 Back to table of contents

wsdl2h -e

This option removes the prefix qualifier from enumeration names.

Without this option all enumeration names are prefixed by their enum name to ensure that enumeration names do not clash with other constants and enumeration names.

For example:

1 <xsd:schema targetNamespace="urn:example" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2  <xsd:simpleType name="engine">
3  <xsd:restriction base="xsd:string">
4  <xsd:enumeration value="ON"/>
5  <xsd:enumeration value="OFF"/>
6  </xsd:restriction>
7  </xsd:simpleType>
8  <xsd:simpleType name="light">
9  <xsd:restriction base="xsd:string">
10  <xsd:enumeration value="ON"/>
11  <xsd:enumeration value="OFF"/>
12  </xsd:restriction>
13  </xsd:simpleType>
14 </xsd:schema>

By default without this option, this schema is translated by wsdl2h to the following interface header file declarations:

enum ns__engine { ns__engine__ON, ns__engine__OFF };
enum ns__light { ns__light__ON, ns__light__OFF, ns__light__BROKEN };

By contrast, with wsdl2h -e option -e we obtain:

enum ns__engine { ON, OFF };
enum ns__light { ON_, OFF_, BROKEN };

Where enumeration names are suffixed with underscores to make them unique.

Note that C++11 scoped enumerations can be used with wsdl2h -c++11 option -c++11, which makes option -e useless.

🔝 Back to table of contents

wsdl2h -F

This option produces interface header files with struct/class declarations that simulate inheritance using transient pointer members to derived types. This option is particularly useful for C source code generation when derived types are required by the application. Derived type values are indicated by xsi:type attributes in XML with the derived type name.

This option can also be used for C++ to replace class inheritance by simulated inheritance using transient pointer members in base classes that point to the value of a derived type, meaning that the base class instance is replaced by the derived class instance. This option also removes pointers from array and container item types. These pointers are normally added to ensure containers can contain derived type values, but pointers are no longer needed by the simulated approach that add pointer members to the base classes.

For example:

1 <xsd:schema targetNamespace="urn:example" xmlns:tns="urn:example" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2  <xsd:complexType name="base">
3  <xsd:sequence>
4  <xsd:element name="value" type="xsd:int"/>
5  </xsd:sequence>
6  </xsd:complexType>
7  <xsd:complexType name="derived1">
8  <xsd:complexContent>
9  <xsd:extension base="tns:base">
10  <xsd:sequence>
11  <xsd:element name="name" type="xsd:string"/>
12  </xsd:sequence>
13  </xsd:extension>
14  </xsd:complexContent>
15  </xsd:complexType>
16  <xsd:complexType name="derived2">
17  <xsd:complexContent>
18  <xsd:extension base="tns:base">
19  <xsd:sequence>
20  <xsd:element name="x" type="xsd:float"/>
21  </xsd:sequence>
22  </xsd:extension>
23  </xsd:complexContent>
24  </xsd:complexType>
25  <xsd:complexType name="derived3">
26  <xsd:complexContent>
27  <xsd:extension base="tns:derived1">
28  <xsd:sequence>
29  <xsd:element name="x" type="xsd:float"/>
30  </xsd:sequence>
31  </xsd:extension>
32  </xsd:complexContent>
33  </xsd:complexType>
34 </xsd:schema>

By default without this option, this schema is translated by wsdl2h -c option -c to the following interface header file declaration in C that lacks inheritance:

struct ns__base
{
int value;
};
struct ns__derived1
{
int value; // base type value of ns__base
char *name; // extension
};
struct ns__derived2
{
int value; // base type value of ns__base
float x; // extension
};
struct ns__derived3
{
int value; // derived1 type value of ns__base
char *name; // derived1 type
float x; // extension
};

By contrast, with wsdl2h -c -F option -F we obtain an interface header file with simulated inheritance using transient pointer members of base types pointing to derived types:

struct ns__base
{
[ struct ns__derived1 *ns__derived1; ] // points to derived type
[ struct ns__derived2 *ns__derived2; ] // points to derived type
int value;
};
struct ns__derived1
{
int value; // base type value of ns__base
char *name; // extension
[ struct ns__derived3 *ns__derived3; ] // points to derived type
};
struct ns__derived2
{
int value; // base type value of ns__base
float x; // extension
};
struct ns__derived3
{
int value; // derived1 type value of ns__base
char *name; // derived1 type
float x; // extension
};

Each transient pointer member name that is used to point to a derived type must match the type name as shown, but trailing underscores are allowed in the member name and type name, to prevent name clashes.

This latter form supports xsi:type attributes in XML with the derived type name to replace base type values by derived type values at runtime by setting one of the transient pointer members to non-NULL. For example, assume ns:data has a base type ns__base (i.e. declared as struct ns__base data) then the following is legal and serializable:

1 <ns:data>
2  <value>123</value>
3 </ns:data>

This is serialized XML for data.value with data.ns__derived1 and data.ns__derived2 both set to NULL.

1 <ns:data xsi:type="ns:derived1">
2  <value>123</value>
3  <name>abc</name>
4 </ns:data>

This is serialized XML for data.ns__derived1->value and data.ns__derived1->name where data.ns__derived1 is non-NULL, for example allocated and set with data.ns__derived1 = soap_new_ns__derived1(soap).

1 <ns:data xsi:type="ns:derived2">
2  <value>123</value>
3  <x>3.14</x>
4 </ns:data>

This is serialized XML for data.ns__derived2->value and data.ns__derived2->x where data.ns__derived1 is NULL and data.ns__derived2 is non-NULL, for example allocated and set with data.ns__derived2 = soap_new_ns__derived2(soap).

1 <ns:data xsi:type="ns:derived3">
2  <value>123</value>
3  <name>abc</name>
4  <x>3.14</x>
5 </ns:data>

This is serialized XML for data.ns__derived1->ns__derived3->value, data.ns__derived1->ns__derived3->name, and data.ns__derived1->ns__derived3->x where data.ns__derived1 and data.ns__derived1->ns__derived3 are non-NULL.

Note that C++ class inheritance achieves the same results for base and derived types, but without the use of transient pointer members. However, this requires container values to be pointers to support type derivation (class members are already pointers), as generated by wsdl2h for C++.

🔝 Back to table of contents

wsdl2h -f

This option removes C++ class inheritance to produce a flat C++ class hierarchy similar to structs in C as generated by wsdl2h.

As a side effect, derived type values can no longer be serialized in place of base type values, see also wsdl2h -F option -F.

Basically this option removes support for xsi:type in XML that indicates a derived type that is restricted or extended from its base type.

This option also removes pointers from array and container item types, because there are no derived types that could extend the item value types. These pointers are normally added to ensure containers can contain derived type values in addition to the base type values.

For example:

1 <xsd:schema targetNamespace="urn:example" xmlns:tns="urn:example" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2  <xsd:complexType name="base">
3  <xsd:sequence>
4  <xsd:element name="value" type="xsd:int"/>
5  </xsd:sequence>
6  </xsd:complexType>
7  <xsd:complexType name="derived1">
8  <xsd:complexContent>
9  <xsd:extension base="tns:base">
10  <xsd:sequence>
11  <xsd:element name="name" type="xsd:string"/>
12  </xsd:sequence>
13  </xsd:extension>
14  </xsd:complexContent>
15  </xsd:complexType>
16  <xsd:complexType name="derived2">
17  <xsd:complexContent>
18  <xsd:extension base="tns:base">
19  <xsd:sequence>
20  <xsd:element name="x" type="xsd:float"/>
21  </xsd:sequence>
22  </xsd:extension>
23  </xsd:complexContent>
24  </xsd:complexType>
25  <xsd:complexType name="derived3">
26  <xsd:complexContent>
27  <xsd:extension base="tns:derived1">
28  <xsd:sequence>
29  <xsd:element name="x" type="xsd:float"/>
30  </xsd:sequence>
31  </xsd:extension>
32  </xsd:complexContent>
33  </xsd:complexType>
34 </xsd:schema>

By default without this option, this schema is translated to the following interface header file declaration in C++ with base and derived classes:

class ns__base
{ public:
int value;
};
class ns__derived1 : public ns__base
{ public:
char *name; // extension
};
class ns__derived2 : public ns__base
{ public:
float x; // extension
};
class ns__derived3 : public ns__derived1
{ public:
float x; // extension
};

By contrast, with wsdl2h -f option -f we obtain an interface header file without inheritance but with classes that are extended with the base class members:

class ns__base
{ public:
int value;
};
class ns__derived1
{ public:
int value; // base type value of ns__base
char *name; // extension
};
class ns__derived2
{ public:
int value; // base type value of ns__base
float x; // extension
};
class ns__derived3
{ public:
int value; // derived1 type value of ns__base
char *name; // derived1 type
float x; // extension
};

This former form supports xsi:type attributes in XML with the derived type name to replace base type values by derived type values at runtime. But this latter form does not support xsi:type attributes in XML and only the base class can be serialized, for example ns__base data:

1 <ns:data>
2  <value>123</value>
3 </ns:data>

This is serialized XML for data.value.

1 <ns:data xsi:type="ns:derived1">
2  <value>123</value>
3  <name>abc</name>
4 </ns:data>

This example can be deserialized when SOAP_XML_STRICT is not enabled, but only the value is retained in data.value. However, when SOAP_XML_STRICT is enabled, deserialization fails due to the additional element name that is rejected.

🔝 Back to table of contents

wsdl2h -g

This option adds global top-level element and attribute declarations to the interface header file generated by wsdl2h.

For example:

1 <xsd:schema targetNamespace="urn:example" xmlns:tns="urn:example" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2  <xsd:attribute name="type" type="xsd:QName"/>
3  <xsd:element name="data" type="tns:record"/>
4  <xsd:complexType name="record">
5  <xsd:sequence>
6  <xsd:element name="name" type="xsd:string"/>
7  <xsd:element name="value" type="xsd:int"/>
8  </xsd:sequence>
9  </xsd:complexType>
10 </xsd:schema>

By default without this option, this schema is translated to the following C++ interface header file (the C interface header file is similar) that declares the ns__record type for tns:record but does not declare attribute tns:type and element tns:data:

typedef std::string xsd__QName;
class ns__record
{ public:
std::string name;
int value;
};

By contrast, with wsdl2h -g option -g we obtain an interface header file with the attribute and element declarations:

typedef std::string xsd__QName;
class ns__record
{ public:
std::string name;
int value;
};
typedef ns__record _ns__data;
typedef xsd__QName _ns__type;

This defines _ns__type and _ns__data, where the latter can be used as a root element to serialize its content with the soapcpp2-generated readers and writers:

struct soap *soap = soap_new();
_ns__data data;
if (soap_read__ns__data(soap, &data))
... // error
if (soap_write__ns__data(soap, &data))
... // error

which parses and re-writes the XML fragment:

1 <ns:data xmlns:ns="urn:example">
2  <name>abc</name>
3  <value>123</value>
4 </ns:data>

Note that top-level element and attribute type names start with an underscore to distinguish them from types. This convention is also used by soapcpp2 to generate schemas that define top-level attributes and elements.

Note that a schema may define a global top-level element with a local type, for example:

1 <xsd:schema targetNamespace="urn:example" xmlns:tns="urn:example" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2  <xsd:element name="record">
3  <xsd:complexType>
4  <xsd:sequence>
5  <xsd:element name="name" type="xsd:string"/>
6  <xsd:element name="value" type="xsd:int"/>
7  </xsd:sequence>
8  </xsd:complexType>
9  </xsd:element>
10 </xsd:schema>

This schema is translated to the following C++ interface header file (the C interface header file is simular) that declares the _ns__record type and element for the tns:record top-level element:

class _ns__record
{ public:
std::string name;
int value;
};

In this case option -g has no effect, because tns:record has a local type that may be used elsewhere in the schema.

🔝 Back to table of contents

wsdl2h -h

This option displays help info and then exits.

🔝 Back to table of contents

wsdl2h -I

This option specifies a directory path to search for WSDL and XSD files.

For example:

wsdl2h -I path file.wsdl

This searches path for .wsdl and .xsd files that are imported by file.wsdl and by other imported files.

When a WSDL or XSD file imports another file then:

  • a file name referenced by http:// or by https:// is retrieved from the specified URL.
  • a file name referenced by file:// is retrieved from the path specified relative to the directory in which wsdl2h is run and the -I option can be used to change that location to import from.
  • a file name without a path (i.e. has no /) or a file name with path stating with ../ are considered files located at relative path locations with respect to the current WSDL and XSD that is importing this file
  • otherwise, imported files are considered relative to the directory in which wsdl2h is run and the -I option can be used to change that location to import from.

WSDL and XSD files that import other WSDL and XSD files typically use relative paths, at least that is recommended by best practices. If absolute paths are used then wsdl2h may fail to find the imported WSDLs and XSDs. This option resolves relative paths but does not help to resolve absolute paths. In the worst case one must edit the WSDLs and XSDs to refer to proper file locations.

🔝 Back to table of contents

wsdl2h -i

This option skips over schema import and as a result none of the imported schemas and their components are imported.

There are two reasons to use this option:

  • when imported components are already declared in interface header files that are imported into the main interface header file with #import, and
  • when imported schemas are explicitly provided with the wsdl2h command as command line arguments, which means that the specified schemas will be used instead of the imported schemas. This may help to resolve issues when imported files are not found by wsdl2h. The schema targetNamespace namespace names are relevant when schemas reference imported schemas by their namespace, not the schema file name.

🔝 Back to table of contents

wsdl2h -j

This option skips the generation of SOAP_ENV__Header and SOAP_ENV__Detail structure definitions, assuming that these are manually replaced in the generated interface header file for soapcpp2.

🔝 Back to table of contents

wsdl2h -k

This option skips the generation of mustUnderstand qualifiers for SOAP_ENV__Header members. This removes the mustUnderstand="true" XML attributes from SOAP Headers in SOAP messages. As per SOAP standard, SOAP Headers with mustUnderstand="true" must not be ignored by receivers.

🔝 Back to table of contents

wsdl2h -L

This option generates less documentation by removing generic @note comments from the interface header file output, thereby reducing the size of the output without removing critical information.

🔝 Back to table of contents

wsdl2h -l

This option displays license information.

🔝 Back to table of contents

wsdl2h -M

This option suppresses the wsdl2h error message

"must understand element with wsdl:required='true'"

This error indicates that a (special) WSDL construct was used that is marked wsdl:required="true", meaning that must not be ignored by the WSDL processor (unless the developer knows what he or she is doing).

🔝 Back to table of contents

wsdl2h -m

This option tells wsdl2h to use xsd.h to define the primitive XSD types instead of generating them in the interface header file for soapcpp2. This option offers an alternative to the use of typemap.dat to redefine primitive XSD types by defining them all together instead of on a type-by-type basis. The interface header file output by wsdl2h includes #import "xsd.h".

🔝 Back to table of contents

wsdl2h -N

This option specifies a name to be used as a service namespace prefix for each WSDL binding.

By default without this option, the wsdl2h tool warns when it reads one or more WSDLs that define multiple bindings:

Warning: 3 service bindings found, but collected as one service (use option -Nname to produce a separate service for each binding)

This means that all 3 services will be collected under one name. When proxy and service classes are generated with soapcpp2 -i option -i or with soapcpp2 -j option -j then the service operations are collected into one proxy and service class. Essentially only one namespace is used. This may lead to clashes when multiple bindings define the same Web service operations (name clashes are resolved by wsdl2h by adding trailing underscores).

By contrast, with wsdl2h -N name option -N name we obtain an interface header file that uses the specified name as a prefix to define the service bindings and service operations.

For example:

wsdl2h -N foo file.wsdl

If file.wsdl has multiple bindings, then the Web service operations associated with each binding are identified by their prefix foo1, foo2, foo3, and so on. As a result, we obtain more than one proxy and service class generated by soapcpp2, one for each binding.

🔝 Back to table of contents

wsdl2h -n

This option changes the default ns namespace prefix to the specified prefix name.

By default without this option, the XML namespace prefix is ns which results in the generation of prefixes ns1, ns2, ns3, and so on.

For example:

wsdl2h -n foo file.wsdl

This generates namespace prefixes foo1, foo2, foo3, and so on.

Warning
It is strongly recommended to define namespace prefixes in the typemap.dat file to prevent future runs of wsdl2h to produce namespace prefixes that are not in the same original order. For example when the order of WSDLs and XSDs changes or if new WSDLs and XSDs are added. Therefore, do not use this option unless the single WSDL processed by wsdl2h is relatively simple and does not import WSDLs and XSDs.

🔝 Back to table of contents

wsdl2h -O

This option optimizes the generated interface header file:

  • -O1 removes duplicate choice/sequence members;
  • -O2 optimize with -O1 and remove unused schema types (types that are unreachable from top-level schema element and attribute roots);
  • -O3 optimize with -O2 and remove unused schema top-level root attributes;
  • -O4 optimize with -O3 and remove unused schema top-level root elements, only retain the root elements used by WSDLs. Use this option only when converting WSDLs (and their associated XSD schemas) to source code, not when solely converting XSD schemas to source code.

Option -O4 is the most aggressive. When used only for one or more XSDs as input to wsdl2h, the output will be empty because removing the root elements (and attributes) results in removing all types from the schema. However, this option is safe to use with WSDLs to aggressively remove all unused schema components that are unreachable from the Web service operation parameter elements and types. Option -O3 is safe to use with one or more XSDs as input to wsdl2h instead of WSDLs, for example when developing an XML application that serializes data as XML root elements (wsdl2h -g option -g is recommended in this case).

Optimization by schema slicing removes unused types, which are types that are unreachable from top-level schema element and attribute roots. A type is marked as used when:

  • it is explicitly used by one or more top-level elements and attributes;
  • it is used as a type by a child element or attribute of a complexType that is marked as used;
  • it is used as the base type of an extension or restriction of a simpleType or complexType that is marked as used.

Marking proceeds recursively until no more types can be marked. All remaining unused types are removed. Top-level elements and attributes are selectively marked unused and removed depending on the level of optimization applied, with -O3 removing unused top-level attributes and -O4 removing unused top-level elements except for all elements used in the specified WSDLs.

Aggressive optimization with options -O2, -O3, and -O4 removes derived type extensions of a base type when the derived types are not marked as used. However, in certain messaging scenarios this may have the undesired effect that this limits the choice of derived types that can be used to replace a base type in XML messages, because a derived type may have been removed when it is not marked as used elsewhere in the WSDLs and XSD schemas. A derived type that replaces a base type in an XML message is indicated by a xsi:type attribute with the QName value of the derived type. The wsdl2h tool generates a C++ class hierarchy to support type derivation, so assigning a derived type value instead of a base type value to a pointer member is automatically serialized in XML with the specified derived value (which is indicated by xsi:type attribute in the XML message). For C applications, we should use wsdl2h -c -F option -c and option -F to simulate inheritance in C. In both cases it is recommended to use the following options to retain all derived type extensions of a base type that is marked as used:

  • -Ow2 optimize with -O2 to remove unused schema types, but retain types that are derived types of base types that are marked as used. .
  • -Ow3 optimize with -O3 to remove unused schema top-level root attributes, but retain types that are derived types of base types that are marked as used..
  • -Ow4 optimize with -O4 to remove unused schema top-level root elements, but retain types that are derived types of base types that are marked as used.

This permits a base type value (typically a struct or class member that is a pointer to a base type) to be assigned a derived type in C++, which is serialized in XML with a xsi:type attribute to indicate the type of the derived value. Likewise, XML data with derived type values are deserialized to C/C++ data automatically. Inheritance is simulated in C, see option -F.

🔝 Back to table of contents

wsdl2h -o

This option specifies a file name for the wsdl2h interface header file output.

By default without this option, the wsdl2h tool writes the interface header file to the file named after the first file name input at the command line, but using .h as the file name extension.

When the input to the wsdl2h tool consists of URLs, the wsdl2h tool writes its output to standard output (usually the screen). Use this option to specify a file instead.

For example:

wsdl2h calc.wsdl

This saves calc.h because the first file specified on the command line is calc.wsdl.

Option -o should be used when a URL is specified on the command line:

wsdl2h -o calc.h http://www.genivia.com/calc.wsdl

This saves the interface header file calc.h.

🔝 Back to table of contents

wsdl2h -P

This option disables the generation of types inherited from the xsd__anyType base type.

This option has effect only when wsdl2h -p option -p is used or when the wsdl2h tool detects that xsd:anyType is used (thereby implicitly and automatically enabling option -p), which means that xsd__anyType should be a base type for all possible types defined in the schemas.

🔝 Back to table of contents

wsdl2h -p

This option makes all types inherit xsd__anyType to support full polymorphism.

This option is automatically enabled when the wsld2h tool detects that xsd:anyType is used, which means that xsd__anyType should be a base type for all possible types defined in the schemas. To disable, use wsdl2h -P option -P.

For example:

1 <xsd:schema targetNamespace="urn:example" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2  <xsd:element name="data" type="data"/>
3  <xsd:complexType name="data">
4  <xsd:sequence>
5  <xsd:element name="value" type="xsd:string"/>
6  <xsd:element name="item" type="xsd:anyType" minOccurs="0" maxOccurs="unbounded"/>
7  </xsd:sequence>
8  </xsd:complexType>
9 </xsd:schema>

This schema is translated to the following C++ interface header file that declares the xsd__anyType type with _XML simpleContent (meaning that __item contains element content as per gSOAP convention) and the ns__data class:

class xsd__anyType
{ public:
_XML __item;
};
class xsd__string_ : public xsd__anyType
{ public:
std::string __item;
};
class ns__data : public xsd__anyType
{ public:
std::string value;
std::vector<xsd__anyType*> item;
};

The xsd__anyType pointer values of the items of the vector can be assigned derived class instances to serialize any type of value declared in the interface header file, including the xsd__string_ wrapper class with simpleContent and the ns__data class with complexContent.

This schema is translated to the following C interface header file with wsdl2h -c -F option -c and option -F to simulate inheritance in C:

struct xsd__string_
{
char* __item;
};
struct xsd__anyType_
{
_XML __item;
[ struct xsd__string_ *xsd__string; ]
[ struct ns__data *ns__data; ]
};
struct ns__data
{
char* value;
$ int __sizeitem;
struct xsd__anyType_* item;
};

The xsd__anyType_ values of items of the dynamic array (item points to an array of size __sizeitem which is a special member to indicate dynamic arrays) can be assigned base xsd__anyType_ and derived types, see wsdlh2 -F option -F.

🔝 Back to table of contents

wsdl2h -Q

This option makes xsd__anySimpleType equal to xsd__anyType to use as the base type for derivation. This option is more effective when used with wsdl2h -p option -p for C++ applications and wsdl2h -F option -F for C applications. This option can also be used with wsdl2h -d option -d to make xsd__anySimpleType equal to a DOM node.

Without option -Q, the xsd__anySimpleType type is just a C/C++ string generated by wsdl2h:

typedef char* xsd__anySimpleType; // in case of C
typedef std::string xsd__anySimpleType; // in case of C++

The reason for this choice is that some WSDLs and XSD schemas use xsd:anySimpleType to declare XML attributes of any type (because XML attributes must be simple types xsd:anyType is invalid to use for attributes). The values of XML attributes of type xsd:anySimpleType can be any character data essentially. There is no mechanism to indicate the actual type of the attribute value used, unlike elements that are annotated with xsi:type attribute with the derived type as its QName value. Therefore, by considering xsd__anySimpleType just strings we can provide any value for XML attributes of type xsd:anySimpleType.

However, there are other uses of xsd__anySimpleType in XSD schemas, where essentially xsd__anySimpleType serves the same purpose as xsd__anyType to provide a base type for derived types, but restricts the derived types to simple types.

Unfortunately, these two cases clash: we want to use C/C++ strings for XML attributes of type xsd:anySimpleType and also use xsd:anySimpleType as a base class for derived types.

Option -Q enables the latter case by making xsd__anySimpleType equal to xsd__anyType so that elements of type xsd:anySimpleType can be serialized with a derived type, using inheritance in C++ and by using simulated inheritance in C using wsdl2h -F option -F.

For example, option -Q changes this generated code for C++ applications:

class xsd__anyType
{ public:
_XML __item;
struct soap *soap;
};
typedef std::string xsd__anySimpleType;
class ns__record : public xsd__anyType
{ public:
xsd__anySimpleType value; // non-polymorphic xsd:anySimpleType value
}

into:

class xsd__anyType
{ public:
_XML __item;
struct soap *soap;
};
class ns__record : public xsd__anyType
{ public:
xsd__anyType *value; // polymorphic xsd:anySimpleType value
}

where all other classes generated by wsdl2h option -p are derived from xsd__anyType, meaning that value can be assigned any one of these classes as long as the class is a simple type wrapper (wsdl2h generates comments to indicate that the polymorhpic value should be a xsd:anySimpleType).

Similar code is generated by wsdl2h option -F for C applications.

On the other hand this option invalidates XML attributes of type xsd:anySimpleType. The soapcpp2 tool warns about this invalid attribute type as a result.

🔝 Back to table of contents

wsdl2h -q

This option specifies a C++ namespace name. The interface header file declarations are placed in the given C++ namespace.

For example:

wsdl2h -q api file.wsdl

The generated interface header file for soapcpp2 places all declarations in the api C++ namespace:

namespace api {
...
}
Warning
It is more difficult to use SOAP Headers and SOAP Faults when C++ namespaces are used. When wsdl2h finds SOAP Headers and SOAP Fault Details it collects these into SOAP_ENV__Header and SOAP_ENV__Detail structures, which become part of the given C++ namespace. However, to use the SOAP_ENV__Header and SOAP_ENV__Detail structures these should be declared at the global scope. This option places these structures with the types used by their members in the given C++ namespace, making them unavailable to the global scope.

See How to build a client or server in a C++ code namespace for details on using C++ namespaces to build client and server applications, which requires a env.h file with SOAP Header and Fault definitions to be compiled with:

 soapcpp2 -penv env.h

The generated envC.cpp file holds the SOAP Header and Fault serializers and you can link this file with your client and server applications.

This option has no effect for C source code output.

🔝 Back to table of contents

wsdl2h -R

This option enables the generation of REST service operations in the interface header file saved by wsdl2h for soapcpp2.

By default without this option, REST service operations defined in one or more WSDLs are ignored.

With this option, both REST and SOAP service operations are declared in the interface header file.

🔝 Back to table of contents

wsdl2h -r

This option specifies a proxy host name and port number with proxy credentials to connect to web sites through a proxy server.

This option can also be used to specify credentials to access a web site that requires authentication (HTTP basic or digest authentication).

For example:

wsdl2h -r proxy.example.org:80:proxyuserid:proxypasswd -r userid:passwd

🔝 Back to table of contents

wsdl2h -S

This option renames the soap members of the generated C++ classes in the interface header file for soapcpp2.

By default without this option, wsdl2h adds struct soap *soap members to classes and structs. This member points to the soap context that manages the instance, when the instance was allocated by the gSOAP engine.

To remove the struct soap *soap members use this option with an empty name:

wsdl2h -S '' file.wsdl

To rename the struct soap *soap members, specify a name for the member, for example ctx:

wsdl2h -S ctx file.wsdl

This option has no effect for C source code output.

🔝 Back to table of contents

wsdl2h -s

This option does not generate C++ std data types and replaces std::vector and std::string by C-like equivalents and is intended for systems with limited support for C++ libraries.

The std::vector struct/class member is replaced by a dynamic array, declared with a __size member followed by a pointer member to the array items.

The std::string is replaced by char*.

This option has no effect for C source code output.

🔝 Back to table of contents

wsdl2h -t

This option specifies an alternate file or path for typemap.dat. See typemap.dat file.

For example:

wsdl2h -t $GSOAP/gsoap/typemap.dat file.wsdl

🔝 Back to table of contents

wsdl2h -U

This option allows UTF-8-encoded Unicode C/C++ identifier names in the generated interface header file for soapcpp2. This assumes that the C/C++ compiler that is used to compile a gSOAP client or server application supports Unicode identifier names.

By default without this option, Unicode XML names in WSDLs and XSDs are preserved using the gSOAP convention for UCS-2 characters in identifier names with _xHHHH where HHHH is a hexadecimal Unicode character code point.

With this option, Unicode XML names in WSDLs and XSDs are preserved "as is" in C/C++ identifier names.

🔝 Back to table of contents

wsdl2h -u

This option replaces unions with structs/classes in the generated interface header file for soapcpp2. Union members are used to represent xsd:choice of elements. A choice of elements can also be represented by pointer members of a struct/class such that only one member is non-NULL. However, when using a struct/class instead of a union, the deserialization validator will not reject additional elements when present.

For example:

1 <xsd:schema targetNamespace="urn:example" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2  <xsd:element name="data" type="data"/>
3  <xsd:complexType name="data">
4  <xsd:sequence>
5  <xsd:element name="value" type="xsd:string"/>
6  <xsd:choice>
7  <xsd:element name="string" type="xsd:string"/>
8  <xsd:element name="number" type="xsd:float"/>
9  </xsd:choice>
10  </xsd:sequence>
11  </xsd:complexType>
12 </xsd:schema>

By default without this option, wsdl2h generates a tagged union for the xsd:choice, where the tag is a special member int __union_data that is set to SOAP_UNION__ns__union_data_string when the string union member is valid or SOAP_UNION__ns__union_data_number when the number union member is valid:

class ns__data
{ public:
std::string value;
$ int __union_data;
union _ns__union_data
{
std::string* string;
float number;
} union_data;
};

With this option, wsdl2h removes the union and replaces it with pointer members to produce a simpler structure:

class ns__data
{ public:
std::string value;
// BEGIN CHOICE <xs:choice>
std::string* string; // Choice of element (one of multiple choices)
float* number; // Choice of element (one of multiple choices)
// END OF CHOICE
};
Warning
This option removes the uniqueness check on choices from the deserialization validator. When serializing data, only one of the choice pointer members should be non-NULL.

🔝 Back to table of contents

wsdl2h -V

This option displays the current wsdl2h tool version and then exits.

🔝 Back to table of contents

wsdl2h -v

This option enables verbose output to assist in debugging the wsdl2h tool.

🔝 Back to table of contents

wsdl2h -W

This option suppresses all warnings produced by wsdl2h. Errors are not suppressed.

🔝 Back to table of contents

wsdl2h -w

This option wraps response parameters in a response struct.

The last parameter of a service operation declared as a function in the interface header file is the response parameter. When multiple response parameters are returned by the service operation or if the response parameter is a complexType (a struct or class), then the parameters should be wrapped in a special "response struct". However, if a single response parameter is a primitive type value then this parameter does not need to be wrapped in a response struct.

This option consistently wraps response parameters in a response struct, even when a single response parameter is a primitive type value.

🔝 Back to table of contents

wsdl2h -X

Document/literal wrapped patterns may cause ambiguities with respect to message namespace qualification. A part name associated with a type is implicitly qualified by the targetNamespace of the WSDL but may also be associated with the namespace of the type. By default, the wsdl2h tool uses the namespace of the type when the type is not a primitive XSD type, otherwise the WSDL targetNamespace is used.

As an example of a document/literal wrapped pattern message, consider:

1 <wsdl:types>
2  <xs:schema ...>
3  <xs:complexType name="Record">
4  ...
5 </wsdl:types>
6 ...
7 <wsdl:message name="Message">
8  <wsdl:part name="Name" type="xs:string"/>
9  <wsdl:part name="Info" type="tns:Record"/>
10 </wsdl:message>
11 ...
12 <wsdl:operation name="Operation">
13  <wsdl:input message="tns:Message"/>
14  <wsdl:output message="tns:MessageResponse"/>
15 </wsdl:operation>
16 ...
17 <wsdl:binding name="Binding" type="tns:PortType">
18  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
19  <wsdl:operation name="Operation">
20  <soap:operation soapAction="Action"/>
21  <wsdl:input>
22  <soap:body use="literal"/>
23  </wsdl:input>
24  <wsdl:output>
25  <soap:body use="literal"/>
26  </wsdl:output>
27  </wsdl:operation>
28  ...

Note that message name="Message" has two parts with both a type, which makes these part namespaces amiguous. The generated interface header file declares a wrapper for the Name request message and the Info response message:

int __ns1__Operation(
std::string Name,
ns1__Record ns2__Info
);

Here, Name belongs to the ns1 namespace, i.e. by the __ns1__Operation, whereas Info belongs to the ns2 namespace. The __ns1__Operation is just a wrapper for the operation and is not visible in XML. Only Name and Info are serialized in XML as the request and response message, respectively.

With option -X the ns2 qualifier is removed:

int __ns1__Operation(
std::string Name,
ns1__Record Info
);

Now both Name and Info belong to the ns1 namespace, i.e. by the __ns1__Operation.

However, best practices for document/literal messaging recommend to avoid this wrapped pattern construct in favor of using elements defined in schemas:

1 <wsdl:types>
2  <xs:schema ...>
3  <xs:element name="Name" type="xs:string"/>
4  <xs:element name="Info" type="tns:Record"/>
5  <xs:complexType name="Record">
6  ...
7  ...
8 </wsdl:types>
9 ...
10 <wsdl:message name="Message">
11  <wsdl:part name="Name" element="tns:Name"/>
12  <wsdl:part name="Info" element="tns:Record"/>
13 </wsdl:message>
14 ...
15 <wsdl:operation name="Operation">
16  <wsdl:input message="tns:Message"/>
17  <wsdl:output message="tns:MessageResponse"/>
18 </wsdl:operation>
19 ...
20 <wsdl:binding name="netfileOverSoap" type="tns:netfileOverSoap">
21  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
22  <wsdl:operation name="Operation">
23  <soap:operation soapAction="Action"/>
24  <wsdl:input>
25  <soap:body use="literal"/>
26  </wsdl:input>
27  <wsdl:output>
28  <soap:body use="literal"/>
29  </wsdl:output>
30  </wsdl:operation>
31  ...

The elements Name and Record are the actual message names, qualified by the schema's targetNamespace:

int __ns1__Operation(
std::string ns2__Name,
ns1__Record ns2__Info
);

See also wsdl2h option -z7.

🔝 Back to table of contents

wsdl2h -x

This option removes _XML type members of structs and classes that are generated for xsd:any and xsd:anyAttribute components.

There are two options to represent xsd:any and xsd:anyAttribute components: the literal _XML string type with XML content (a char* string) or a DOM node. DOM nodes are generated for xsd:any and xsd:anyAttribute components with wsdl2h -d option -d, which also defines xsd:anyType as the DOM node xsd__anyType in C and C++.

🔝 Back to table of contents

wsdl2h -y

This option adds typedef synonyms for structs and enums to the interface header file, which is useful for C source code. A typedef synonym for a struct is declared by typedef struct name name; and for an enum is declared by typedef enum name name;.

🔝 Back to table of contents

wsdl2h -z

These options are for backward compatiility with older gSOAP releases:

  • -z1 compatibility with 2.7.6e: generate pointer-based arrays
  • -z2 compatibility with 2.7.15: (un)qualify element/attribute referenced members
  • -z3 compatibility with 2.7.16 to 2.8.7: (un)qualify element/attribute referenced members
  • -z4 compatibility up to 2.8.11: don't generate union structs in std::vector
  • -z5 compatibility up to 2.8.15: don't include minor improvements
  • -z6 compatibility up to 2.8.17: don't include minor improvements
  • -z7 compatibility up to 2.8.59: don't generate std::vector of class of union
  • -z8 compatibility up to 2.8.74: don't generate qualifiers for doc/lit wrapped patterns
  • -z9 compatibility up to 2.8.93: always qualify element/attribute referenced members, even when defined in the same namespace with default forms unqualified
  • -z10 compatibility up to 2.8.96: generate qualifiers even when defined without namespace

🔝 Back to table of contents

wsdl2h -_

This option replaces _USCORE by the Unicode _x005f character code point in identifier names in C and C++ in the generated interface header file.

🔝 Back to table of contents

Customizing XML data bindings with the typemap.dat file

The typemap.dat file for the wsdl2h tool can be used to customize or optimize the type bindings by mapping schema types to C/C++ types. This file contains custom XML schema to C/C++ type bindings and XML namespace bindings for namespace prefixes to be generated by the wsdl2h tool. You can edit this file to enable features such as custom serializers for schema types, C++11 smart pointers to replace regular pointers, bind XML namespace prefixes to XML namespace URIs, and specify bindings for schema types.

Here is a simple example of a typemap.dat file:

#       This file contains custom definitions of the XML Schema types and 
#       C/C++ types for your project, and XML namespace prefix definitions. 
#       The wsdl2h WSDL importer consults this file to determine bindings. 
[ 
// This comment will be included in the generated .h file 
// You can include any additional declarations, includes, imports, etc. 
// within [ ] sections. The brackets must appear at the start of a line 
] 
#       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"

The i and s prefixes are declared such that the header file output by wsdl2h uses these prefixes instead of the default ns1, ns2, etc. It is strongly recommended to name the prefixes in this way, because future runs of wsdl2h may result in a different assignment of the default ns1, ns2, ... prefixes. Therefore, it is recommended that application code should not rely on the default prefixes.

Type bindings can be provided to bind XML schema types to C/C++ types for your project. These type bindings have four parts:

prefix__type = declaration | use | ptr-use

where prefix__type is the C/C++ type name of the schema type (using gSOAP's type naming conventions), the declaration part declares the C/C++ type in the generated header file which may be empty to omit, the optional use part specifies how the type is used by other types such as by member declarations and as function parameters, and the optional ptr-use part specifies how the type is used as a pointer type by other types and as function parameters.

#       Example XML Schema and C/C++ type bindings: 
xsd__int          = | int 
xsd__string       = | char* | char* 
xsd__boolean      = enum xsd__boolean { false_, true_ }; | enum xsd__boolean 
xsd__base64Binary = class xsd__base64Binary { unsigned char *__ptr; int __size; }; | xsd__base64Binary | xsd__base64Binary
#       You can extend structs and classes with member data and functions. 
#       For example, adding a constructor to ns__myClass:
ns__myClass       = $ ns__myClass(); 
#       The general form is
#       class_name = $ member; 

XML Schema types are associated with an optional C/C++ type declaration, a use reference, and a pointer-use reference. The pointer-use reference of the xsd__byte type for example, is int* because char* is reserved for strings.

For example, you can replace the std::string that used by default for C++ with a wide string:

xsd__string = | std::wstring

Or replace the char* strings that are used by default for C with wchar_t*:

xsd__string = | wchar_t* | wchar_t*

When the ptr-use part is not specified, it will be auto-generated as pointer T* for use type T or std::shared_ptr<T> when the variable $POINTER = std::shared.

The declaration part need not be empty, for example if a type must be declared. For example:

xsd__string = typedef std::string mystring; | mystring | std::optional<mystring>

When a type binding requires only the use part to be changed, the declaration part can be an ellipsis ..., as in:

prefix__type = ... | use | ptr-use

The ... ellipsis ensures that the wsdl2h-generated type definition is preserved, while the use and ptr-use parts are amended as specified.

This method is useful to serialize types dynamically, when XML elements carry the xsi:type attribute indicating the type of element content. The following illustrates an "any" type mapping for the ns:sometype XSD type in a schema. This type will be replaced with a "any" type wrapper that supports dynamic serialization of element types indicated by the xsi:type attribute:

[ 
struct __any 
{
  int __type;   // set to a SOAP_TYPE_T value
  void *__item; // points to data of type T as serialized per SOAP_TYPE_T
} 
] 
xsd__anyType = ... | struct __any | struct __any

where __type and __item are used to serialize any data type in the wrapper. The __item member points to the value (de)serialized, with the type of this value indicated by __type which is a SOAP_TYPE_T value for type named T.

To match an element with content to (de)serialize, rename the __item member to the XML element name, as usual.

Additional members can be specified to extend a generated struct or class. Class and struct extensions are of the form:

prefix__type = $ member-declaration

For example, to add getter and setter methods to class myns__record (see also Section Get and set methods):

myns__record = $ int get(struct soap *soap) const;
myns__record = $ int set(struct soap *soap);

Another way to use typemap.dat is to remap one C/C++ type to another type:

prefix__type1 == prefix__type2

This replaces prefix__type1 by prefix__type2 in the wsdl2h output. For example:

SOAP_ENC__boolean == xsd__boolean

where SOAP_ENC__boolean is replaced by xsd__boolean, which in turn may be mapped to a C enum xsd__boolean type or C++ bool type.

The $CONTAINER variable defines the container type to use in the wsdl2h-generated declarations for C++, which is std::vector by default. For example, to use std::list as the container in the wsdl2h-generated declarations we add the following line to typemap.dat:

$CONTAINER = std::list

Also a Qt container can be used instead of the default std::vector, for example QVector:

[
#include <QVector>
]
$CONTAINER = QVector

To remove containers, use wsdl2h -s. This also removes std::string, but you can re-introduce std::string with

xsd__string = | std::string

The typemap.dat $POINTER variable defines the smart pointer to use in the wsdl2h-generated declarations for C++, which replaces the use of * pointers. For example:

$POINTER = std::shared_ptr

Not all pointers in the generated output are replaced by smart pointers by wsdl2h, such as pointers as union members and pointers as struct/class members that point to arrays of values.

The variable $SIZE defines the type of array sizes, which is int by default. For example, to change array size types to size_t:

$SIZE = size_t

Permissible types are int and size_t. This variable does not affect the size of dynamic arrays, xsd__hexBinary and xsd__base64Binary types, which is always int.

When C++17 is enabled with wsdl2h and soapcpp2 option -c++17, you can also semi-automatically enable std::optional declarations with optional class and structure member variables. This means that std::optional is used instead of a (smart) pointer to make a member optional.

To enable std::optional with member variables that are primitive types, typedef, and enum automatically:

$OPTIONAL = std::optional

Local unnamed simpleType restrictions may not adopt the specified optional type and still use pointers instead. This limitation may be lifted in a future release.

🔝 Back to table of contents

The soapcpp2 tool

The soapcpp2 tool 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 file.h

where file.h is an interface header file generated by wsdl2h or developed manually to specify the service operations as function prototypes and C/C++ data types to serialize in XML.

The soapcpp2 tool produces the XML data binding implementation source code, client-side stub functions, and server-side skeleton functions.

The type of files generated by soapcpp2 are:

  • soapStub.h a modified and annotated header file produced from the input interface header file, this file is compilable by C/C++ compilers while the input interface header file is not.
  • soapH.h the main header file to be included by the application source code, this file also includes soapStub.h.
  • soapC.cpp (or .c for C) the serializers for the C/C++ types specified in the interface header file.
  • soapClient.cpp (or .c for C) the client-side stub functions to invoke remote service operations.
  • soapServer.cpp (or .c for C) the server-side skeleton functions to dispatch service requests to user-define service functions.
  • soapClientLib.cpp (or .c for C) the client-side stub functions combined with local static serializers to be integrated as one big "library".
  • soapServerLib.cpp (or .c for C) the service-side skeleton functions combined with local static serializers to be integrated as one big "library"
  • soapXYZProxy.h the C++ proxy class declaration generated with soapcpp2 -i or soapcpp2 -j
  • soapXYZProxy.cpp the C++ proxy class implementation generated with soapcpp2 -i or soapcpp2 -j
  • soapXYZService.h the C++ service class declaration generated with soapcpp2 -i or soapcpp2 -j
  • soapXYZService.cpp the C++ service class implementation generated with soapcpp2 -i or soapcpp2 -j
  • *.xsd files are generated containing XML schemas for each namespace prefix ns used in the interface header file input to the soapcpp2 tool, see also Section How to generate WSDL service descriptions . Not applicable when the interface header file was generated with wsdl2h.
  • *.wsdl files are generated containing WSDL descriptions for each namespace prefix ns used by service operations in the interface header file input to the soapcpp2 tool, see also Section How to generate WSDL service descriptions . Not applicable when the interface header file was generated with wsdl2h.
  • *.xml files with SOAP or XML request and response messages are generated.
  • *.nsmap the XML namespace mapping table, generated for the first namespace prefix ns found in the interface header file input to the soapcpp2 tool.

If client and service applications are to be developed for the same Web services API then the same interface header file can be used to generate the source code for both the client and the service. There is no need to generate a WSDL with soapcpp2 and then use that WSDL to generate a new interface header file with wsdl2h. The new header file generated by this approach will not be identical to the original header file.

The soapClientLib.cpp and soapServerLib.cpp can be used to build client and server libraries. The serialization functions are declared static to avoid link symbol conflicts. For this approach to compile, we also should create a separate interface header file env.h with SOAP Header and Fault structures with serializers that are non-static, i.e. globally declared and implemented, as described in Section How to create client/server libraries .

The following files are part of the gSOAP source code package and are required to build gSOAP applications:

  • gsoap/stdsoap2.h the header file to include in your source code, but already included when including soapH.h.
  • gsoap/stdsoap2.c the C source code of the entire gSOAP engine.
  • gsoap/stdsoap2.cpp (or .c for C) the source code of the entire gSOAP engine.
  • gsoap/dom.cpp (or .c for C) the source code of the DOM parser, which is optional and only required when using DOM such as with WS-Security.

🔝 Back to table of contents

soapcpp2 options

The soapcpp2 tool supports the following command-line options:

option result
-0 no SOAP, generate REST source code
-1 generate SOAP 1.1 source code
-2 generate SOAP 1.2 source code
-A require HTTP SOAPAction headers to invoke server-side operations
-a use HTTP SOAPAction headers with WS-Addressing to invoke server-side operations
-b serialize byte arrays char[N] as string
-C generate client-side source code only
-c generate C source code
-c++ generate C++ source code (default)
-c++11 generate C++ source code optimized for C++11 (compile with -std=c++11)
-d path use path to save files
-Ec generate extra functions for deep copying
-Ed generate extra functions for deep deletion
-Et generate extra functions for data traversals with callback functions
-e generate SOAP RPC encoding style bindings (also use -1 or -2)
-f N multiple soapC files, with N serializer definitions per file (N>=10)
-g generate XML sample messages in template format for testmsgr
-h display help info and exit
-I path use path(s) for #import (paths separated with :)
-i generate C++ service proxies and objects inherited from soap struct
-j generate C++ service proxies and objects that share a soap struct
-L don't generate soapClientLib and soapServerLib
-l generate linkable modules (experimental)
-m generate source code for the Matlab(tm) MEX compiler (deprecated)
-n use service name to rename service functions and namespace table
-p name save files with new prefix name instead of soap
-Q name use name as the C++ namespace, including custom serializers
-q name use name as the C++ namespace, excluding custom serializers
-r generate soapReadme.md report
-S generate server-side source code only
-s generate stub and skeleton functions with strict XML validation checks
-T generate server auto-test source code
-t generate source code for fully xsi:type typed SOAP/XML messages
-u uncomment WSDL/schema output by suppressing XML comments
-V display the current version and exit
-v verbose output
-w don't generate WSDL and schema files
-x don't generate sample XML message files
-y include C/C++ type access information in sample XML messages
-z1 compatibility: generate old-style C++ service proxies and objects
-z2 compatibility with 2.7.x: omit XML output for NULL pointers
-z3 compatibility up to 2.8.30: _param_N indexing and nillable pointers
-z4 compatibility up to 2.8.105: char* member defaults, even when the XML element is omitted

For example

 soapcpp2 -L -c -d projects -p my -x file.h

This saves the following source code files:

  • projects/myH.h serialization functions, this file should be included in projects.
  • projects/myC.c serialization functions
  • projects/myClient.c client call stub functions
  • projects/myServer.c server request dispatcher
  • projects/myStub.h annotated copy of the source interface header file
  • projects/ns.nsmap namespace table, this file should be included or used in projects.
  • projects/ns.wsdl WSDL with Web service definitions
  • projects/ns.xsd XML schema

Windows users can use the usual / for compile-time flags as well as -, for example:

soapcpp2 -L -c /d projects /p my /x file.h

Options -A, -a, -c, -c++, -c++11, -e, -i, -j, -n, -s, -t, -w, and -x can also be specified in the interface header file for soapcpp2 using the //gsoapopt directive, for example:

// Generate pure C and do not produce WSDL output:
//gsoapopt cw
int ns__webmethod(char *in, char **out);

🔝 Back to table of contents

soapcpp2 -0

This option generates XML REST source code by disabling SOAP bindings, essentially disabling the SOAP protocol and replacing it by direct XML REST messaging.

This option uses soap_set_version at the client side in the generated source code to enable XML REST messaging, disabling SOAP.

In addition, the soapcpp2 tool nullifies the SOAP namespaces from the the generated namespace table file to force a server application that uses this table to use XML REST only:

struct Namespace namespaces[] = {
{ "SOAP-ENV", NULL, NULL, NULL },
{ "SOAP-ENC", NULL, NULL, NULL },
{ "xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance", NULL },
{ "xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema", NULL },
...
{ NULL, NULL, NULL, NULL}
};
Note
Web services applications developed with gSOAP support both REST and SOAP messaging automatically when the namespace table is left intact (i.e. generated without option -0) with the SOAP namespaces present in the table. XML REST request messages are served and REST messages returned. Likewise, SOAP 1.1 request messages are served and SOAP 1.1 messages returned, SOAP 1.2 request messages are served and SOAP 1.2 messages returned.

For example, the following example calculator service SOAP and XML REST request messages are served by a gSOAP service developed with SOAP 1.1 as the default protocol:

1 <SOAP-ENV:Envelope
2  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
3  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
4  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
6  xmlns:ns="urn:calc">
7  <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
8  <ns:add>
9  <a>2</a>
10  <b>3</b>
11  </ns:add>
12  </SOAP-ENV:Body>
13 </SOAP-ENV:Envelope>
1 <ns:add
2  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
4  xmlns:ns="urn:calc">
5  <a>2</a>
6  <b>3</b>
7 </ns:add>

The server returns the following XML SOAP 1.1 and XML REST responses:

1 <SOAP-ENV:Envelope
2  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
3  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
4  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
6  xmlns:ns="urn:calc">
7  <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
8  <ns:addResponse>
9  <result>5</result>
10  </ns:addResponse>
11  </SOAP-ENV:Body>
12 </SOAP-ENV:Envelope>
1 <ns:addResponse
2  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
3  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
4  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
6  xmlns:ns="urn:calc">
7  <result>0</result>
8 </ns:addResponse>

By default all XML namespaces are included with the root element, which improves messaging performance at the sending and receiving sides, because a stack of xmlns binding scopes does not need to be maintained. Use SOAP_XML_CANONICAL to emit xmlns binding pairs when the XML namespace prefix is used. This is slower but may or may not reduce the message size.

🔝 Back to table of contents

soapcpp2 -1

This option forces SOAP 1.1 bindings globally in the generated source code, thereby overriding the SOAP protocol version used in the interface header file input.

This option uses soap_set_version at the client to enable SOAP 1.1 request and response messages, disallowing SOAP 1.2.

In addition, the soapcpp2 tool saves the SOAP 1.1 namespaces in the second column of the generated namespace table file and the SOAP 1.2 in the third column to allow the server to accept SOAP 1.1 and SOAP 1.2 requests:

struct Namespace namespaces[] = {
{ "SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/*/soap-envelope", NULL },
{ "SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/*/soap-encoding", NULL },
{ "xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance", NULL },
{ "xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema", NULL },
...
{ NULL, NULL, NULL, NULL}
};

🔝 Back to table of contents

soapcpp2 -2

This option forces SOAP 1.2 bindings globally in the generated source code, thereby overriding the SOAP protocol version used in the interface header file input.

This option uses soap_set_version at the client to enable SOAP 1.2 request and response messages, disallowing SOAP 1.1.

In addition, the soapcpp2 tool saves the SOAP 1.2 namespaces in the second column of the generated namespace table file and the SOAP 1.1 in the third column to allow the server to accept SOAP 1.1 and SOAP 1.2 requests:

struct Namespace namespaces[] = {
{ "SOAP-ENV", "http://www.w3.org/2003/05/soap-envelope", "http://schemas.xmlsoap.org/soap/envelope/", NULL },
{ "SOAP-ENC", "http://www.w3.org/2003/05/soap-encoding", "http://schemas
{ "xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance", NULL },
{ "xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema", NULL },
...
{ NULL, NULL, NULL, NULL}
};

🔝 Back to table of contents

soapcpp2 -A

This option generates server-side source code that requires HTTP SOAPAction headers to be present. The server invokes server-side operations based on the SOAPAction header value in request messages, instead of the SOAP/XML request message name which is ignored. This option is used with WS-Addressing, WS-ReliableMessaging, and WS-Discovery servers to relay messages based on HTTP SOAPAction headers and/or the SOAP Header wsa:Action when present (the latter requires the [WS-Addressing plugin](wsaplugin)).

Alternatively, use soapcpp2 -a option -a to let the server invoke server-side operations based on the SOAPAction header value in request messages when present, otherwise when not present this lets the server invoke server-side operations based on the SOAP/XML request message name as usual.

This option can also be specified by the //gsoapopt A directive in the interface header file.

🔝 Back to table of contents

soapcpp2 -a

This option generates server-side source code that uses HTTP SOAPAction headers when present to invoke server-side operations based on the SOAPAction header value in request messages, otherwise when not present lets the server invoke server-side operations based on the SOAP/XML request message name as usual. This option is used with WS-Addressing, WS-ReliableMessaging, and WS-Discovery servers to relay messages based on HTTP SOAPAction headers and/or the SOAP Header wsa:Action when present (the latter requires the [WS-Addressing plugin](wsaplugin)).

Alternatively, use soapcpp2 -A option -A to require HTTP SOAPAction headers to be present in SOAP request messages to invoke server-side operations.

This option can also be specified by the //gsoapopt a directive in the interface header file.

🔝 Back to table of contents

soapcpp2 -b

This option serializes byte arrays specified as char[N] as strings. Without this option char[N] is serialized as an array of bytes. Fixed-size arrays specified in the interface header file input are generally serialized as arrays in XML using item elements.

For example:

struct ns__record
{
char bytes[3];
int ints[2];
};

By default without this option the ns__record struct is serialized as:

1 <ns:record>
2  <bytes>
3  <item>65</item>
4  <item>66</item>
5  <item>0</item>
6  </bytes>
7  <ints>
8  <item>1</item>
9  <item>2</item>
10  </ints>
11 </ns:record>

With this option, the ns__record struct is serialized as:

1 <ns:record>
2  <bytes>AB</bytes>
3  <ints>
4  <item>1</item>
5  <item>2</item>
6  </ints>
7 </ns:record>

🔝 Back to table of contents

soapcpp2 -C

This option restricts soapcpp2 to generate client-side source code only. When this option is combined with soapcpp2 -CS option -S, no client and server source code is generated.

🔝 Back to table of contents

soapcpp2 -c -c++ -c++11

Option -c generates C source code, -c++ generates C++ source code, and -c++11 generates C++11 source code.

Note
The //gsoapopt directive in the interface header file takes priority over this option, when c, c++, or c++11 is declared with this directive in the interface header file.

🔝 Back to table of contents

soapcpp2 -d

This option specifies a path to save the generated files. For example:

soapcpp2 -d source file.h

This saves files to the source/ directory located within the current directory, which should exist and should be writable.

🔝 Back to table of contents

soapcpp2 -Ec -Ed -Et

These options generate extra functions for deep copying of serializable C/C++ data, deep deletion of serializable C/C++ data, and deep data traversals with user-defined callback functions over serializable C/C++ data.

For a serializable type T declared in the interface header file for soapcpp2, option -Ec generates:

  • virtual T * T::soap_dup(struct soap*) const where T is a class, returns a duplicate of this object by deep copying, replicating all deep cycles and shared pointers when a managing soap context is provided as argument. Deep copy is a tree when argument is NULL, but the presence of deep cycles will lead to non-termination. Use flag SOAP_XML_TREE with the managing context to copy into a tree without cycles and pointers to shared objects.
  • T * soap_dup_T(struct soap*, T *dst, const T *src) where T is not a class, deep copy src into dst, replicating all deep cycles and shared pointers when a managing soap context is provided as argument. When dst is NULL, allocates space for dst and returns a pointer to the allocated copy. Deep copy results in a tree when the soap context is NULL, but the presence of deep cycles will lead to non-termination. Use flag SOAP_XML_TREE with managing context to copy into a tree without cycles and pointers to shared objects. Returns dst or allocated copy when dst is NULL.

For a serializable type T declared in the interface header file for soapcpp2, option -Ed generates:

  • virtual void T::soap_del() const where T is a class, deletes all heap-allocated members of this object by deep deletion ONLY IF this object and all of its (deep) members are not managed by a soap context AND the deep structure is a tree (no cycles and co-referenced objects by way of multiple (non-smart) pointers pointing to the same data). Can be safely used after T::soap_dup(NULL) to delete the deep copy. Does not delete the object itself.
  • void soap_del_T(const T*) where T is not a class, deletes all heap-allocated members of this object by deep deletion ONLY IF this object and all of its (deep) members are not managed by a soap context AND the deep structure is a tree (no cycles and co-referenced objects by way of multiple (non-smart) pointers pointing to the same data). Can be safely used after soap_dup_T(NULL, NULL, const T*) to delete the deep copy returned. Does not delete the object itself.

For a serializable type T declared in the interface header file for soapcpp2, option -Et generates:

  • virtual void T::soap_traverse(struct soap *soap, const char *tag, soap_walker p, soap_walker q) where T is a class, uses function callbacks p and q to traverse this object by deep ordered tree traversals over its members when non-NULL. Function p is a pre-order function that is called before objects and data are visited recursively and function q is a post-order function that is called after objects and data are visited recursively. Either p or q may be NULL. The tag string is passed to p and q and should not be NULL. Cyclic graphs are treated as trees by pruning pointer back-edges, though this method does not always prevent a data node from being visited twice.
  • void soap_traverse_T(struct soap *soap, T *data, const char *tag, soap_walker p, soap_walker q) where T is not a class, uses function callbacks p and q to traverse this data by deep ordered tree traversals over its members when present and non-NULL. Function p is a pre-order function that is called before objects and data are visited recursively and function q is a post-order function that is called after objects and data are visited recursively. Either p or q may be NULL. The tag string is passed to p and q and should not be NULL. Cyclic graphs are treated as trees by pruning pointer back-edges. though this method does not always prevent a data node from being visited twice.

The pre-order p and post-order q callback functions should be declared as a soap_walker function, which has the following function signature:

void soap_walker(struct soap *soap, void *data, int soap_type, const char *tag, const char *type)

where data points to the data node visited which is of type soap_type (a SOAP_TYPE_T constant), tag is the non-NULL element or attribute tag name (qualified or unqualified), and type is the non-NULL C/C++ type of the data. The void* soap::user member can be used to pass user-defined data to the callbacks.

For example:

// file: record.h
//gsoap ns schema namespace: urn:example
struct ns__record
{
@ char *name;
int value;
struct ns__record *subrecord;
};
soapcpp2 -Ecdt record.h

The main program:

#include "soapH.h"
#include "ns.nsmap"
int main()
{
struct soap *soap = soap_new();
ns__record record; // a serializable type
ns__record rec_dup;
int indent = 0;
soap->recvfd = open(file, O_RDONLY);
if (soap->recvfd < 0)
... // error
if (soap_read_ns__record(soap, &record)) // deserialize from file into managed memory
... // error
close(soap->recvfd);
if (soap_write_ns__record(soap, &record)) // serialize to standard output
... // error
soap->user = (void*)&indent;
soap_traverse_ns__record(soap, record, "record", pre, post);
soap_dup_ns__record(NULL, &rec_dup, &record); // deep copy the record to unmanaged memory
soap_destroy(soap); // delete managed objects
soap_end(soap); // delete managed data and temporaries
soap_free(soap); // free the context
soap_del_ns__record(&rec_dup) // deep delete unmanaged record
}
void pre(struct soap *soap, void *a, int n, const char *s, const char *t)
{
printf("\n%*s%s %s = {", (*(int*)soap->user)++, "", t, s);
if (n == SOAP_TYPE_int)
printf(" %d", *(int*)a);
else if (n == SOAP_TYPE_string)
printf(" %s", *(char**)a ? *(char**)a : "(n/a)");
}
void post(struct soap *soap, void *a, int n, const char *s, const char *t)
{
printf(" }");
(*(int*)soap->user)--;
}

The soap_read_ns__record deserializes the following XML:

1 <ns:record xmlns:ns="urn:example" name="foo">
2  <value>123</value>
3  <subrecord name="bar">
4  <value>456</name>
5  </subrecord>
6 </ns:record>

Then soap_traverse_ns__record call displays the contenst of record using the pre and post walker functions:

struct ns__record record = {
 char * name = { foo }
 int value = { 123 }
 struct ns__record subrecord = {
  char * name = { bar }
  int value = { 456 } } }

🔝 Back to table of contents

soapcpp2 -e

This option forces SOAP RPC encoding bindings globally in the generated source code, when the SOAP messaging style is not declared in the interface header file with directives.

SOAP document/literal style messaging it the default messaging style. The messaging style can be specified with the //gsoap <prefix> service style: and //gsoap <prefix> service encoding: directives. See also SOAP RPC encoded versus document/literal style.

This option can also be specified by the //gsoapopt e directive in the interface header file.

🔝 Back to table of contents

soapcpp2 -f

This option splits the serialization source code saved to soapC.c and soapC.cpp files into multiple soapC_NNN files as specified by the numeric parameter. This option alleviates compilation issues with very large source code files.

For example:

soapcpp2 -f40 file.h

This generates multiple soapC_NNN.cpp files each with 40 serializers, with NNN counting from 001 onward.

The value of this option must be larger or equal to 10.

🔝 Back to table of contents

soapcpp2 -g

This option generates XML sample messages in template format for the gSOAP Test Messenger testmsgr tool to test SOAP and REST XML clients and servers.

By default without this option, soapcpp2 generates sample XML messages with the proper XML structure but without useful data. The Test Messenger tool generates random messages directed by the template parameters included by soapcpp2 -g option -g.

This option only has effect when soapcpp2 -x option -x is not used, which skips the generation of sample messages.

🔝 Back to table of contents

soapcpp2 -h

This option displays help info and then exits.

🔝 Back to table of contents

soapcpp2 -I

This option specifies one or more directory paths to search for imported interface header files. Multiple paths are separated by a colon.

For example:

soapcpp2 -I path1:path2 file.h

This searches path1 and then path2 for files that are imported with #import in file.h.

🔝 Back to table of contents

soapcpp2 -i

This option generates C++ client-side proxy classes and server-side service classes, where the classes inherit the soap context struct with the engine state to handle communications and manage memory independently of other class instances.

By contrast, soapcpp2 -j option -j allows a soap context to be used and reused for multiple proxy and server instances.

This option can also be specified by the //gsoapopt i directive in the interface header file.

This option has no effect for C source code output.

🔝 Back to table of contents

soapcpp2 -j

This option generates C++ client-side proxy classes and server-side service classes, where the classes have a pointer member soap to a soap context struct that handles communications and manages memory.

By contrast to soapcpp2 -i option -i, this option allows a soap context to be used and reused for multiple proxy and server instances.

This option can also be specified by the //gsoapopt j directive in the interface header file.

This option has no effect for C source code output.

🔝 Back to table of contents

soapcpp2 -L

This option skips the generation of the soapClientLib and soapServerLib files. These files are generally not needed to build client and server applications.

These files are useful to compile multiple "libraries" of client and server applications, such that all serialization source code is declared static and kept hidden from the global scope, which makes the serialization functions inaccessible to the global scope to prevent global name clashes.

Alternatively, use soapcpp2 -q name option -q name to develop C++ applications with C++ namespaces to prevent global name clashes.

🔝 Back to table of contents

soapcpp2 -l

This option is experimental and should only be used to generate source code for modules. This option is auto-enabled when a #module directive is found in an interface header file for soapcpp2, see how to build modules and libraries with the #module directive.

🔝 Back to table of contents

soapcpp2 -m

This option to generate source code for the Matlab(tm) MEX compiler is deprecated.

🔝 Back to table of contents

soapcpp2 -n

This option renames the generated service functions soap_serve to name_serve and the generated namespace table namespaces to name_namespaces to the name specified with the soapcpp2 -n -p name option -p name.

This option is useful to prevent name clashes when soapcpp2 is invoked multiple times to generate source code for different parts of an application. See also how to create client/server libraries.

This option can also be specified by the //gsoapopt n directive in the interface header file.

🔝 Back to table of contents

soapcpp2 -p

This option saves source code files with the specified file name prefix name with soapcpp2 -p name instead of soap as the file name prefix.

This option is useful to prevent name clashes when soapcpp2 is invoked multiple times to generate source code for different parts of an application. See also how to create client/server libraries.

For example:

soapcpp2 -p foo file.h

This saves fooStub.h, fooH.h, fooC.cpp, and so on.

When the main application is build from the renamed name-prefixed source code files, plugins and custom serializers that are compiled and linked with the application should include nameH.h instead of soapH.h. This can be done with the -D SOAP_H_FILE=nameH.h option to the C/C++ compiler to rename this file to include instead of soapH.h.

🔝 Back to table of contents

soapcpp2 -Q

This option specifies a C++ namespace name for the generated source code, including for the custom serializers when used. See also soapcpp2 -q name option -q name for details on specifying C++ namespaces.

The source code files are saved with name as prefix instead of soap. This means that all plugins and custom serializers that are compiled and linked with the application should include nameH.h instead of soapH.h. This can be done with the -D SOAP_H_FILE=nameH.h option to the C/C++ compiler to rename this file to include instead of soapH.h.

This option has no effect for C source code output.

🔝 Back to table of contents

soapcpp2 -q

This option specifies a C++ namespace name for the generated source code, excluding the custom serializers when used. See also soapcpp2 -Q name option -Q name.

This option is the same as specifying a C++ namespace in the interface header file that encapsulates all declarations:

namespace name {
... // all of the interface header file content goes here
}

This interface header file format is generated with wsdl2h -q name option -q name.

The source code files are saved with name as prefix instead of soap. This means that all plugins and custom serializers that are compiled and linked with the application should include nameH.h instead of soapH.h. This can be done with the -D SOAP_H_FILE=nameH.h option to the C/C++ compiler to rename this file to include instead of soapH.h.

See How to build a client or server in a C++ code namespace for details on using C++ namespaces to build client and server applications, which requires a env.h file with SOAP Header and Fault definitions to be compiled with:

 soapcpp2 -penv env.h

The generated envC.cpp file holds the SOAP Header and Fault serializers and you can link this file with your client and server applications.

This option has no effect for C source code output.

🔝 Back to table of contents

soapcpp2 -r

This option generates a soapReadme.md markdown report. This report includes details pertaining the serializable data types and Web client and service operations, covering XML type details, serialization functions, and SOAP/REST API programming details.

The markdown report is readable as it is, but can be converted to HTML for improved readability with Doxygen or with pandoc, or can be browsed in Firefox with https://www.genivia.com/files/readmeviewer.html.zip.

🔝 Back to table of contents

soapcpp2 -S

This option restricts soapcpp2 to generate server-side source code only. When this option is combined with soapcpp2 -CS option -C, no client and server source code is generated.

🔝 Back to table of contents

soapcpp2 -s

This option generates client-side stub functions and proxy classes, server-side skeleton functions and service classes with strict XML validation checks enabled. This option effectively hard-codes the SOAP_XML_STRICT run time mode flag.

This option can also be specified by the //gsoapopt s directive in the interface header file.

Warning
This option is not recommended for SOAP RPC encoding style messaging, but XML REST and SOAP/XML document/literal style messages can be validated.

🔝 Back to table of contents

soapcpp2 -T

This option generates server auto-test source code. The generated source code implements a test server soapTester.c (for C) or soapTester.cpp (for C++) that can be deployed to echo client requests, for example for testing purposes.

For example:

soapcpp2 -T file.h
c++ -o tester soapTester.cpp soapServer.cpp soapC.cpp stdsoap2.cpp
./tester 8192 8080

This runs the tester server on port 8080 with soap context initialization mode flag 8192 = 0x2000 = SOAP_XML_INDENT.

See generating an auto test server for client testing for more details. More advanced servers for testing are available with the gSOAP Test Messenger testmsgr tool to test SOAP and REST XML clients and servers.

🔝 Back to table of contents

soapcpp2 -t

This option generates source code to fully annotate SOAP/XML messages with xsi:type attribute values. This option is useful for SOAP RPC encoded messaging with SOAP applications that require xsi:type attributes for all XML elements in SOAP messages.

This option can also be specified by the //gsoapopt t directive in the interface header file.

🔝 Back to table of contents

soapcpp2 -u

This option uncomments WSDL and XSD files generated by soapcpp2 by supressing the inclusion of `` comments to annotate WSDL and XSD files.

🔝 Back to table of contents

soapcpp2 -V

This option displays the current soapcpp2 tool version and then exits.

🔝 Back to table of contents

soapcpp2 -v

This option enables verbose output to assist in debugging the soapcpp2 tool.

🔝 Back to table of contents

soapcpp2 -w

This option skips the generation of WSDL and XSD files.

This option can also be specified by the //gsoapopt w directive in the interface header file.

🔝 Back to table of contents

soapcpp2 -x

This option skips the generation of sample XML message files.

This option can also be specified by the //gsoapopt x directive in the interface header file.

🔝 Back to table of contents

soapcpp2 -y

This option adds C/C++ type information to the sample XML message files generated by soapcpp2.

🔝 Back to table of contents

soapcpp2 -z

These options are for backward compatiility with older gSOAP releases:

  • -z1 compatibility: generate old-style C++ service proxies and objects
  • -z2 compatibility with 2.7.x: omit XML output for NULL pointers
  • -z3 compatibility up to 2.8.30: _param_N indexing; nillable pointers
  • -z4 compatibility up to 2.8.105: char* member defaults, even when the XML element is omitted

🔝 Back to table of contents

The #import directive

The #import directive is used to include interface header files into other interface header files for soapcpp2. By contrast, the #include directive (and #define directive for that matter) is moved by the soapcpp2 tool into the generated source code file soapStub, see Section The #include and #define directives .

The #import directive is used for two purposes: we use it to include the contents of one interface header file into another interface header file and to import a module, see Section How to build modules and libraries with the #module directive .

An example of the #import directive:

#import "mydefs.h"
int ns__webmethod(ns__record *in, struct ns__webmethodResponse { ns__record out; } *out);

where "mydefs.h" is an interface header file that defines ns__record:

struct ns__record
{
const char *name;
const char *address;
};
typedef struct ns__record ns__record;

🔝 Back to table of contents

The #include and #define directives

The #include and #define directives are copied by the soapcpp2 tool into the generated source code. These directives are added to the top of the generated soapStub.h before any other header file is included. Therefore, #include and #define directives can be used to influence the generated source code files.

The following example interface header file for soapcpp2 refers to std::ostream:

#include <ostream>
#define SOME_VALUE 123
// std::ostream can't be serialized, but need to be declared to make it visible:
extern class std::ostream;
class ns__myClass
{
public:
virtual void print(std::ostream &s) const; // we need std::ostream here
... //
};

This example also uses an #include and a #define directive that will be added to the top of soapStub.h before gsoap/stdsoap2.h is included.

Warning
Using #define to override WITH_MACRO and SOAP_MACRO compile-time flags is not recommended because the gsoap/stdsoap2.cpp (gsoap/stdsoap2.c for C) is used to build the -lgsoap++ (and -lgsoap for C) library, which is not affected by these macros whereas the soap context is, as used by the application, leading to predictable crashes. Use SOAPDEFS_H or WITH_SOAPDEFS_H to define macros that are visible to all source code compiled.

🔝 Back to table of contents

Service operation specification format

A service operation is specified as a function prototype in an interface header file for soapcpp2. For the function prototypes specified, the soapcpp2 tool generates client stub functions to invoke remote services and generates server skeleton functions to implement services.

The service operation specified by a function prototype should return int, which is either SOAP_OK for success and a soap_status error code for failure, see Section Run-time error codes .

The general format of a service operation specification is:

int prefix__method_name(inparam1, inparam2, ..., inparamn, outparam);

where

  • prefix__ is the XML namespace prefix of the method
  • method_name is the service operation name
  • inparam1, ..., inparamn are the input parameters to the service operation, which are either values or pointer types, but not references
  • outparam is the single output parameter of the service operation, which must be a pointer or a reference type.

A single output parameter is specified and multiple output parameters should be wrapped in a struct or class, see Section Service operation parameter passing . The fully qualified name of the function namespace_prefix__method_name must be unique and cannot match the name of a struct, class, or enum declared in the same header file.

The method request is send as an XML message using the qualified function name with the input parameters in XML:

1 <prefix:method-name>
2  <inparam1>...</inparam1>
3  <inparam2>...</inparam2>
4  ...
5  <inparamn>...</inparamn>
6 </prefix:method-name>

where the inparam1, ..., inparamn elements are the XML element representations of the inparam parameter name declarations.

The XML response by the Web service is of the form:

1 <prefix:method-nameResponse>
2  <outparam>...</outparam>
3 </prefix:method-nameResponse>

where the outparam element is the XML element representation of the outparam parameter name declaration, see Section C/C++ identifier name to XML tag name translation . By convention, the response element name is the method name ending in Response. See Section Service operation parameter passing on how to change the declaration if the service response element name is different.

With SOAP messaging the request and response XML messages are placed in the SOAP-ENV:Envelope and SOAP-ENV:Body elements. SOAP 1.1 document/literal messaging is the default messaging mode in gSOAP, which are modified to SOAP or REST with //gsoap <prefix> service method-protocol: directives, see Section Directives.

The soapcpp2 tool generates a client stub function for the service operation. This stub is of the form:

int soap_call_prefix__method_name(struct soap *soap, char *endpoint, char *action, inparam1, inparam2, ..., outparam);

This stub is called by a client application to perform the service operation call.

The soapcpp2 tool generates a skeleton functions for the service operation. The skeleton function called by soap_serve is:

int soap_serve_prefix__method_name(struct soap *soap);

which after deserializing the XML request message calls the prefix__method_name service operation defined by the service application and serializes the XML response message when the service operation returns SOAP_OK.

Alternatively, soapcpp2 -j option -j or option -i generates a C++ client proxy class and a service class. These classes have methods corresponding to the service operations, which on the client side can be invoked to invoke remote service operations and on the server side are implemented by the service application to execute the service operations.

🔝 Back to table of contents

Service operation parameter passing

The input parameters of a service operation must be passed by value or by pointer. Input parameters cannot be passed by reference. Passing a pointer to the data is preferred when the size of the data of the parameter is non trivial such as values of primitive type.

The output parameter must be passed by pointer or by reference.

The input and output parameter types must be serializable, which means that there are some limitations on the types of data that can be passed, see Section Limitations .

If the output parameter is a pointer or reference to a struct or class type, it is considered a service operation 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 members are the output parameters of the service operation, see also Section How to change the response element name . Therefore, if the output parameter has to be a struct or class, a response struct or class must be declared to wrap that struct or class type parameter. Likewise, if a service operation returns multiple output parameters then a response struct or class should be used to wrap the output parameters. By SOAP conventions, the response element is the service operation name ending with "<i>`Response`</i>".

The general form of a response struct or class wrapper is:

struct prefix__method_nameResponse
{
outparam1;
outparam2;
... //
outparamn;
};

where

  • prefix__ is the optional namespace prefix of the response element.
  • response_element_name it the name of the response element.
  • outparam1, ..., outparamn are the output parameters of the service operation.

The general form of a service operation specification with a response element declaration is:

int prefix__method_name(inparam1, inparam2, ..., inparamn, struct prefix__method_nameResponse { outparam1; outparam2; ...; outparamn; } *anyname);

The choice of name for anyname has no effect on the SOAP encoding and decoding and is only used as a place holder for the response. In C++ this parameter can be passed by reference instead of by pointer.

The request message is:

1 <prefix:method-name>
2  <inparam1>...</inparam1>
3  <inparam2>...</inparam2>
4  ...
5  <inparamn>...</inparamn>
6 </prefix:method-name>

where the inparam1, ..., inparamn elements are the XML element representations of the inparam parameters.

The response message is of the form:

1 <prefix:method-nameResponse>
2  <outparam1>...</outparam1>
3  <outparam2>...</outparam2>
4  ...
5  <outparamn>...</outparamn>
6 </prefix:method-nameResponse>

where the outparam1, ..., outparamn elements are the XML element representations of the outparam parameters.

The input and 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 How to specify anonymous parameter names .

🔝 Back to table of contents

C/C++ identifier name to XML tag name translation

One of the nice aspects of gSOAP is its powerful C/C++ XML data binding and the flexibility to specify names for XML, such as service operation names, class names, type identifiers, and struct or class members. The first aspect is the use of namespace prefixes with C/C++ names to qualify the names with XML namespaces, which is specified with a prefix__ or as we will see later can be specified with a colon prefix: in the C/C++ name. A C/C++ identifier name of the form

prefix__element_name

is be encoded in XML as

1 prefix:element-name

The underscore pair (__) separates the namespace prefix from the element name. Each namespace prefix has a namespace URI specified by a //gsoap <prefix> schema namespace: <URI> directive that is saved to the soapcpp2-generated namespace mapping table, see Sections XML namespaces and the namespace mapping table and XML namespace considerations . The namespace URI is a unique identification that can be associated with the service operations and data types. The namespace URI disambiguates potentially identical service operation names and data type names used by disparate organizations.

XML element names are XSD NCNames (non-colon names) that may contain letters, digits, underscores, hyphens, dots, and other special characters except reserved characters and colon. To add non-element names of service operations, structs, classes, typedefs, and members can be A single underscore _ in a C/C++ prefix or identifier name is replaced by a hyphen - in the XML encoding. For example, the identifier name SOAP_ENC__ur_type is represented in XML as SOAP-ENC:ur-type. A _DOT is replaced by a dot . in XML, and _USCORE is replaced by an underscore _ in XML. For example:

class n_s__biz_DOTcom
{
char * n_s__biz_USCOREname;
};

is serialized in XML as:

1 <n-s:biz.com>
2  <n-s:biz_name>Bizybiz</n-s:biz_name>
3 </n-s:biz.com>

Other special characters are added to C/C++ names as _xHHHH where HHHH is the hexadecimal code of a Unicode character code point.

Trailing underscores in an identifier name are stripped from the XML encoding. This is useful when an identifier name clashes with a C++ keyword. For example, return may be used as an XML element. This return element can be specified as return_, for example as a struct or class member or function parameter.

By default the soapcpp2 tool generates data binding source code in which all local XML elements are and attributes are unqualified:

//gsoap x schema namespace: urn:x
struct x__record
{
@ char * type; // maps to unqualified type attribute
char * name; // maps to unqualified name element
};

where the name element and the type attribute are unqualified in the XML content (for example to facilitate SOAP RPC encoding).

To force qualification of elements and attributes, use the "form" directive:

//gsoap x schema namespace: urn:x
//gsoap x schema form: qualified
struct x__record
{
@ char * type; // maps to qualified x:type attribute
char * name; // maps to qualified x:name element
};

You can also use "elementForm" and "attributeForm" directives to (un)qualify local element and attributes, respectively.

Because the soapcpp2-generated serializers follow the qualified/unqualified forms of the schemas, there is normally no need to explicitly qualify struct/class members because automatic encoding rules will be used.

If explicit qualification is needed, this can be done using the prefix convention:

//gsoap x schema namespace: urn:x
//gsoap y schema namespace: urn:y
struct x__record
{
@ char * xsi__type; // maps to qualified xsi:type attribute
char * y__name; // maps to qualified y:name element
};

which ensures that there cannot be any name clashes between members of the same name defined in different schemas (consider for example name and y__name), but this can clutter the representation when clashes do not occur.

An alternative to the prefix convention is the use of "colon notation" in the interface header file for soapcpp2. This extra addition to the the C/C++ syntax allows you to bind type names and struct and class members to qualified and unqualified XML tag names explicitly, thus bypassing the default mechanism that automatically qualifies or unqualifies element and attribute tag names based on the schema element or attribute forms.

The colon notation for type names, struct and class names, and members overrides the prefix qualification rules explicitly:

//gsoap x schema namespace: urn:x
//gsoap y schema namespace: urn:y
struct x:record
{
@ char * xsi:type; // maps to qualified xsi:type attribute
char * y:name; // maps to qualified y:name element
};

where x and y are namespace prefixes that are declared with a directive. The xsi:type member is an XML attribute in the xsi namespace. The soapcpp2 tool generates data binding implementation source code with the following cleaned-up struct without the annotations:

// This code is generated in soapStub.h:
struct record
{
char * type; /* optional attribute of type xsd:string */
char * name; /* optional element of type xsd:string */
};

The soapcpp2 tool also generates XML schemas with element and attribute references. That is, y:name is referenced from the y schema by the x:record complexType defined in the x schema.

The colon notation also allows you to override the element and attribute forms to unqualified for qualified schemas:

//gsoap x schema namespace: urn:x
//gsoap x schema form: qualified
struct x:record
{
@ char * :type; // maps to unqualified type attribute
char * :name; // maps to unqualified name element
};

where the colon notation ensures that both type and name are unqualified in the XML content, which overrides the default qualified forms of the x schema.

Note that the use of colon notation to bind namespace prefixes to type names (typedef, enum, struct, and class names) translates to code without the prefixes. This means that name clashes can occur between types with identical unqualified names:

enum x:color { RED, WHITE, BLUE };
enum y:color { YELLOW, ORANGE }; // illegal enum name: name clash with x:color

while prefixing with double underscores never lead to clashes:

enum x__color { RED, WHITE, BLUE };
enum y__color { YELLOW, ORANGE }; // no name clash

Also note that colon notation has a very different role than the C++ scope operator ::. The scope operator cannot be used in places where we need colon notation, such as struct and class member members.

The default mechanism that associates XML tag names with the names of struct and class member members can be overridden by "re-tagging" names with the annotation of a tag placed next to the member member name. This is particularly useful to support legacy code for which the fixed naming of member members cannot be easily changed. For example:

//gsoap x schema namespace: urn:x
//gsoap x schema form: qualified
struct x:record
{
@ char * t `:type`; // maps to unqualified type attribute
char * s `name`; // maps to qualified x:name element
};

This maps the t member to the type XML attribute tag and s member to the x:name XML element tag. Tags will be namespace qualified as per schema element and attribute forms, unless preceded by a colon.

As of gSOAP 2.8.23 and greater, Unicode characters in C/C++ identifiers are accepted by soapcpp2 when the source file is encoded in UTF-8. C/C++ Unicode names are mapped to Unicode XML tags. For C/C++ source code portability reasons, the wsdl2h tool still converts Unicode XML tag names to ASCII C/C++ identifiers using the _xHHHH naming convention for HHHH character code points. Use wsdl2h -U option -U to map Unicode letters in XML tag names to UTF-8-encoded Unicode letters in C/C++ identifiers.

🔝 Back to table of contents

Generating a SOAP/XML client application with soapcpp2

After invoking the soapcpp2 tool on an interface header file description of a service to generate soapStub, soapH.h, and soapC.cpp for the XML serializers, and soapClient.cpp for the client stub functions, the client application is compiled in C++ as follows:

 c++ -o myclient myclient.cpp stdsoap2.cpp soapC.cpp soapClient.cpp

For C we use soapcpp2 -c option -c to generate C source code that is compiled with:

 cc -o myclient myclient.c stdsoap2.c soapC.c soapClient.c

Depending on your system configuration, such as with Unix, linking with -lsocket, -lxnet, and -lnsl may be required.

The myclient.cpp file should include soapH.h and must include or define a global namespace mapping table, unless WITH_NONAMESPACES is used.

For examples of SOAP and REST client applications, see gsoap/samples in the gSOAP source code package. The online getting-started guide covers example client and server applications in C and C++, visit https://www.genivia.com/dev.html to read more. Various examples ranging from simple calculator service APIs to very large protocols spanning dozens of WSDLs can be found at https://www.genivia.com/examples.html

To test client applications using an auto-generated echo test server, use soapcpp2 -T option -T, see the next section. You can also test a client application with the gSOAP Test Messenger.

🔝 Back to table of contents

Generating a SOAP/XML Web service application with soapcpp2

After invoking the soapcpp2 tool on an interface header file description of a service to generate soapStub, soapH.h, and soapC.cpp for the XML serializers, and soapServer.cpp for the server skeleton functions, the service application is compiled in C++ as follows:

 c++ -o myserver myserver.cpp stdsoap2.cpp soapC.cpp soapServer.cpp

For C we use soapcpp2 -c option -c to generate C source code that is compiled with:

 cc -o myserver myserver.c stdsoap2.c soapC.c soapServer.c

Depending on your system configuration, such as with Unix, linking with -lsocket, -lxnet, and -lnsl may be required.

The myserver.cpp file should include soapH.h and should include or define a global namespace mapping table, unless WITH_NONAMESPACES is used.

A gSOAP service can be installed as:

To test a service, see the gSOAP Test Messenger.

Furthermore, an echo test server application soapTester.cpp is generated with soapcpp2 -T option -T, which is a stand-alone iterative test server that echos SOAP/XML requests and runs on the specified port. Compile this with:

 c++ -o testserver soapTester.cpp stdsoap2.cpp soapC.cpp soapServer.cpp

Then run on a port, say 8080:

 ./testServer 12288 8080

The 12288 value is a combination of the SOAP_XML_INDENT (0x2000) and SOAP_XML_STRICT (0x1000) integer flag values (8192 + 4096 = 12288).

For examples of SOAP and REST Web service applications, see gsoap/samples in the gSOAP source code package. The online getting-started guide covers example client and server applications in C and C++, visit https://www.genivia.com/dev.html to read more. Various examples ranging from simple calculator service APIs to very large protocols spanning dozens of WSDLs can be found at https://www.genivia.com/examples.html

🔝 Back to table of contents

Generating an auto test server for client testing

The soapcpp2 -T option -T generates an echo test server application source code soapTester.cpp, which is to be compiled and linked with the code generated for a server implementation soapServer.cpp (or with the generated service class file) and soapC.cpp. The feature also supports C source code, use the soapcpp2 -c -T options -c and -T to generate a C test server.

The echo test server can be used to test a client application, by the client sending messages to the echo test server that echos responses back to the client. These responses are structurally valid but may lack sufficient details to consider the response messages useful.

The generated source code is compiled with:

c++ -o tester soapTester.cpp soapServer.cpp soapC.cpp stdsoap2.cpp

To run the tester auto-test service on a port to test a client against, use two command-line arguments: the first argument is a combined integer of OR-ed values of the context flags such as 12288 which is a combination of SOAP_XML_INDENT (0x1000 = 4096) and SOAP_XML_STRICT (0x1000 = 8196) and the second argument is the port number:

 ./tester 12288 8080

This starts an iterative stand-alone server on port 8080. Messages can be sent to http://localhost:8080 to test a client application against the echo test server. The data in the response messages are copied from the request messages when possible, or XML default values, or empty otherwise.

More advanced servers for testing are available with the gSOAP Test Messenger testmsgr tool to test SOAP and REST XML clients and servers.

🔝 Back to table of contents

Generating deep copy and deletion functions

The soapcpp2 -Ec option -Ec generates deep copy code for each serializable type T declared in an interface header file for soapcpp2. The soapcpp2 -Ed option -Ed generates deep deletion code.

For a serializable type T declared in the interface header file for soapcpp2, option -Ec generates:

  • virtual T * T::soap_dup(struct soap*) const where T is a class, returns a duplicate of this object by deep copying, replicating all deep cycles and shared pointers when a managing soap context is provided as argument. Deep copy is a tree when argument is NULL, but the presence of deep cycles will lead to non-termination. Use flag SOAP_XML_TREE with the managing context to copy into a tree without cycles and pointers to shared objects.
  • T * soap_dup_T(struct soap*, T *dst, const T *src) where T is not a class, deep copy src into dst, replicating all deep cycles and shared pointers when a managing soap context is provided as argument. When dst is NULL, allocates space for dst and returns a pointer to the allocated copy. Deep copy results in a tree when the soap context is NULL, but the presence of deep cycles will lead to non-termination. Use flag SOAP_XML_TREE with managing context to copy into a tree without cycles and pointers to shared objects. Returns dst or allocated copy when dst is NULL.

For a serializable type T declared in the interface header file for soapcpp2, option -Ed generates:

  • virtual void T::soap_del() const where T is a class, deletes all heap-allocated members of this object by deep deletion ONLY IF this object and all of its (deep) members are not managed by a soap context AND the deep structure is a tree (no cycles and co-referenced objects by way of multiple (non-smart) pointers pointing to the same data). Can be safely used after T::soap_dup(NULL) to delete the deep copy. Does not delete the object itself.
  • void soap_del_T(const T*) where T is not a class, deletes all heap-allocated members of this object by deep deletion ONLY IF this object and all of its (deep) members are not managed by a soap context AND the deep structure is a tree (no cycles and co-referenced objects by way of multiple (non-smart) pointers pointing to the same data). Can be safely used after soap_dup_T(NULL, NULL, const T*) to delete the deep copy returned. Does not delete the object itself.

For example:

#include "soapH.h"
#include "ns.nsmap"
int main()
{
struct soap *soap = soap_new();
ns__record record; // a serializable type
ns__record rec_dup;
soap->recvfd = open(file, O_RDONLY);
if (soap->recvfd < 0)
... // error
if (soap_read_ns__record(soap, &record)) // deserialize from file into managed memory
... // error
close(soap->recvfd);
soap_dup_ns__record(NULL, &rec_dup, &record); // deep copy the record to unmanaged memory
soap_destroy(soap); // delete managed objects
soap_end(soap); // delete managed data and temporaries
soap_free(soap); // free the context
soap_del_ns__record(&rec_dup) // deep delete unmanaged record
}

🔝 Back to table of contents

Serialization and deserialization rules

This section describes the serialization and deserialization of C and C++ data types in XML, and therefore by implication in SOAP 1.1 and 1.2. First, the difference between SOAP RPC encoding and document/literal style is explained and how to switch between SOAP 1.1 and 1.2 or support both in applications. Then the general XML representations of C/C++ data in XML and XML schema is explained.

To obtain more information about the code generated by soapcpp2 for the data types specified in an interface header file for soapcpp2, use soapcpp2 -r option -r to generate a soapReadme.md report with all the details.

For additional details on serialization of data types in XML, see the C and C++ XML Data Bindings documentation.

🔝 Back to table of contents

SOAP RPC encoding versus document/literal style messaging

The serialization and deserialization rules is almost identical for these two different styles, except for the following:

  • With SOAP RPC encoding, generic complexTypes with maxOccurs="unbounded" are not allowed and SOAP-encoded arrays must be used instead.
  • XML attributes and unions (XML schema choice) are not allowed with SOAP RPC encoding.
  • In XML messages conforming to SOAP RPC encoding we often see xsi:type attributed messages although the xsi:type attribute is not required and the gSOAP engine does not produce xsi:type attributes unless required, e.g. to identify derived classes from base classes serialized. Use soapcpp2 -t option -t to force xsi:type attributes in the XML output.
  • In XML messages conforming to SOAP RPC encoding, multi-reference accessors using id-href/ref attributes are common to encode co-referenced data. By contrast, multi-referenced data is not accurately represented in document/literal style, which means that data structure graphs cannot be accurately serialized. Document/literal is strictly "tree shaped".

The soapcpp2 tool uses SOAP 1.1 document/literal style by default. Use the //gsoap directives to control the SOAP protocol version and messaging style, see Section Directives, or use soapcpp2 options -2 (SOAP 1.2), -e (encoding style), and -t (add xsi:type attributes).

🔝 Back to table of contents

SOAP 1.1 versus SOAP 1.2 and dynamic switching

SOAP 1.1 is the default protocol. SOAP 1.2 support is automatically turned on when the appropriate SOAP 1.2 namespace is used in the WSDL input to wsdl2h, which then generates #import "soap12.h" in the interface header file:

#import "soap12.h"

This interface header file when input to soapcpp2 results in a XML namespace mapping table with SOAP 1.2 namespaces:

struct Namespace namespaces[] =
{
{ "SOAP-ENV", "http://www.w3.org/2003/05/soap-envelope" },
{ "SOAP-ENC", "http://www.w3.org/2003/05/soap-encoding" },
... //
{ NULL, NULL }
}

The soapcpp2-generated default SOAP 1.1 namespace table allow for dynamic switching between SOAP 1.1 to SOAP 1.2 by providing the SOAP 1.2 namespace as a pattern in the third column of a namespace table:

struct Namespace namespaces[] =
{
{ "SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/*/soap-encoding" },
{ "SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/*/soap-envelope" },
... //
{ NULL, NULL }
}

where the * in the third column of the namespace URI pattern is a wildcard for any sequence of character. This is used to match inbound xmlns XML namespace bindings that are then associated with the prefix in the table. For example, when the inbound XML contains a xmlsn:soap="http://www.w3.org/2003/05/soap-envelope" binding then the soap prefix used in the inbound XML is actually equivalent to SOAP-ENV used in gSOAP as determined by the matching pattern in the third column of the XML namespace table shown above.

In this way, gSOAP Web services can respond to both SOAP 1.1 or SOAP 1.2 requests. Moreover, the gSOAP engine will automatically return a SOAP 1.2 message response to a SOAP 1.2 message request when the XML namespace table shown above is used. This works by using the specified pattern in the third column, when it matches the namespace URI of the inbound XML request message of course. However, the use of SOAP 1.1 or 1.2 is overridden for one or more service operations with the //gsoap <prefix> service method-protocol: directive.

A gSOAP client that sends a request message will always send it using the SOAP protocol specified by the namespace in the second column, unless this is overridden with a //gsoap <prefix> service method-protocol: directive.

To make the XML namespace table available to the developer, the soapcpp2 tool generates a .nsmap file with the SOAP-ENV and SOAP-ENC namespaces and patterns as shown in the example above.

To use SOAP 1.2 by default and accept SOAP 1.1 messages to be received, use the soapcpp2 -2 option -2 to generate SOAP 1.2 .nsmap and .wsdl files. Alternatively, add the following line to your interface header file (generated by wsdl2h) for soapcpp2:

#import "soap12.h"

The soap12.h file is located in gsoap/import.

Warning
SOAP 1.2 does not support SOAP "partially transmitted arrays". So the __offset member of a dynamic array is meaningless in SOAP 1.2.
SOAP 1.2 uses SOAP_ENV__Code, SOAP_ENV__Reason, and SOAP_ENV__Detail members of a SOAP_ENV__Fault fault struct, while SOAP 1.1 uses faultcode, faultstring, and detail members.

🔝 Back to table of contents

Primitive type serialization

The default encoding rules for primitive C and C++ data types in XML are given in the table below:

C/C++ type XSD type
bool xsd:boolean
char and int8_t xsd:byte
short and int16_t xsd:short
int, long, and int32_t xsd:int
LONG64, long long and int64_t xsd:long
unsigned char and uint8_t xsd:unsignedByte
unsigned short and uint16_t xsd:unsignedShort
unsigned int, unsigned long and uint32_t xsd:unsignedInt
ULONG64, long long, and uint64_t xsd:unsignedLong
float xsd:float
double xsd:double
long double xsd:decimal with #import "custom/long_double.h"
time_t xsd:dateTime
struct tm xsd:dateTime with #import "custom/struct_tm.h"
struct timeval xsd:dateTime with #import "custom/struct_timeval.h"
char*, const char*, and std::string xsd:string
wchar_t*, const wchar_t*, and std::wstring xsd:string

Enumerations and bit masks are also supported, see Section Enumeration serialization .

Custom serializers for long double, struct tm, and struct timeval and many other specialized C and C++ types are available, see the C and C++ XML Data Bindings documentation for details.

The previous table shows how C/C++ primitive types are mapped to XSD types. To define and use the full range of XSD types is done with typedefs to define namespace-qualified types in C/C++ corresponding to the XSD types (or to any schema type for that matter).

XSD types such as xsd:positiveInteger, xsd:anyURI, and xsd:date for which no built-in data structures in C and C++ exist can always be represented by strings and some can be represented by integers or floats. Validation constraints can be added to validate the XSD type values as explained further below.

A typedef in an interface header file for soapcpp2 declares a schema type name. The soapcpp2 tool interprets typedef declarations the same way as a regular C compiler interprets them. However, the soapcpp2 tool also uses the type name when generating WSDLs and XSD files and in xsi:type attributes when present.

For example, the declaration:

typedef uint64_t xsd__positiveInteger;

creates a named type xsd__positiveInteger represented by ulong64_t and serialized as XSD type xsd:positiveInteger.

The built-in primitive and derived numerical XSD 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__. However, the use of SOAP_ENC XML types is obsolete and redundant because XSD primitive types can be used instead.

Other XSD types not mentioned in this section, such as gYearMonth, gYear, gMonthDay, gDay, xsd:gMonth, QName, NOTATION, etc., can be encoded similarly using a typedef declaration with a string type.

For additional in-depth details, see the C and C++ XML Data Bindings documentation.

xsd:anyURI

Represents a Uniform Resource Identifier Reference (URI). Each URI scheme imposes specialized syntax rules for URIs in that scheme, including restrictions on the syntax of allowed fragment identifiers. It is recommended to use strings to store xsd:anyURI XML Schema types. The recommended type declaration is:

typedef char *xsd__anyURI;

or

typedef std::string xsd__anyURI;

xsd:base64Binary

Represents Base64-encoded arbitrary binary data. For using the xsd:base64Binary XSD type, the use of the base64Binary representation of a dynamic array is strongly recommended, see Section base64Binary serialization . However, the type can also be declared as a string and the encoding will be string-based:

typedef char *xsd__base64Binary;

or

typedef std::string xsd__base64Binary;

However, it is the responsibility of the application to make sure the string content is according to the Base64 Content-Transfer-Encoding defined in Section 6.8 of RFC 2045. Better is to use the base64 serializer that serializes binary data as xsd:base64Binary:

struct xsd__base64Binary
{
unsigned char *__ptr; // point to data to serialize
int __size; // length of the data to serialize
};

xsd:boolean

For declaring an xsd:boolean XSD type, the use of a bool is recommended in C++. For C, see Section Boolean enumeration serialization for C . The corresponding type declaration is:

typedef bool xsd__boolean;

xsd:byte

Represents a byte (-128...127). The corresponding type declaration is:

typedef char xsd__byte;

xsd:dateTime

Represents a date and time. The lexical representation is according to the ISO 8601 extended format CCYY-MM-DDThh:mm:ss where "CC" represents the century, "YY" the year, "MM" the month and "DD" the day, preceded by an optional leading "-" sign to indicate a negative number. If the sign is omitted, "+" is assumed. The letter "T" is the date/time separator and "hh", "mm", "ss" represent hour, minute and second respectively. It is recommended to use the time_t type to store xsd:dateTime XSD types and the type declaration is:

typedef time_t xsd__dateTime;

However, note that calendar times before the year 1902 or after the year 2037 cannot be represented. Upon receiving a date outside this range, the time_t value will be set to -1. Also strings can be used to store xsd:dateTime types:

typedef char *xsd__dateTime;

Best is to use a custom serializer struct tm, struct timeval, or std::chrono::system_clock::time_point defined by gsoap/custom/struct_tm.h, gsoap/custom/struct_timeval.h, and gsoap/custom/chrono_timepoint.h to represent xsd:dateTime accurately.

xsd:date

Represents a date. The lexical representation for date is the reduced (right truncated) lexical representation for dateTime: CCYY-MM-DD. It is recommended to use strings (char*) to store xsd:date XSD types. The type declaration is:

typedef char *xsd__date;

Best is to use a custom serializer struct tm defined by gsoap/custom/struct_tm_date.h to represent xsd:date accurately.

xsd:decimal

Represents arbitrary precision decimal numbers. It is recommended to use the {double} type to store xsd:decimal XSD types and the type declaration is:

typedef double xsd__decimal;

Better is to use a custom serializer gsoap/custom/long_double.h to represent xsd:decimal or a string to avoid losing accuracy of very large numbers.

xsd:double

Corresponds to the IEEE double-precision 64-bit floating point type. The type declaration is:

typedef double xsd__double;

xsd:duration

Represents a duration of time. The lexical representation for duration is the ISO 8601 extended format PnYn MnDTnH nMnS, where nY represents the number of years, nM the number of months, nD the number of days, T is the date/time separator, nH the number of hours, nM the number of minutes and nS the number of seconds. The number of seconds can include decimal digits to arbitrary precision. It is recommended to use strings (char*) to store xsd:duration XSD types. The type declaration is:

typedef char *xsd__duration;

Better is to use a custom serializer gsoap/custom/duration.h or gsoap/custom/chrono_duration.h to represent xsd:duration or a string to avoid losing accuracy of very large numbers.

xsd:float

Corresponds to the IEEE single-precision 32-bit floating point type. The type declaration is:

typedef float xsd__float;

xsd:hexBinary

Represents arbitrary hex-encoded binary data. It has a lexical representation where each binary octet is encoded as a character tuple, consisting of two hexadecimal digits ([0-9a-fA-F]) representing the octet code. For example, "0FB7" is a hex encoding for the 16-bit integer 4023 (binary representation is 111110110111. For using the xsd:hexBinary XSD type, the use of the hexBinary representation of a dynamic array is strongly recommended, see Section hexBinary serialization . However, the type can also be declared as a string and the encoding will be string-based:

typedef char *xsd__hexBinary;

or

typedef std::string xsd__hexBinary;

However, it is the responsibility of the application to make sure the string content is hex formatted. Better is to use the hex serializer that serializes binary data as xsd:hexBinary:

struct xsd__hexBinary
{
unsigned char *__ptr; // point to data to serialize
int __size; // length of the data to serialize
};

xsd:int

Corresponds to a 32-bit integer in the range -2147483648 to 2147483647.

typedef int xsd__int;

xsd:integer

Corresponds to an unbounded integer. C/C++ does not support unbounded integers as a standard feature. The recommended type declaration is:

typedef int64_t xsd__integer;

Another possibility is to use strings to represent unbounded integers and do the translation in the application itself.

xsd:long

Corresponds to a 64-bit integer in the range -9223372036854775808 to 9223372036854775807. The type declaration is:

typedef int64_t xsd__long;

xsd:negativeInteger

Corresponds to a negative unbounded integer. C/C++ does not support unbounded integers as a standard feature. The recommended type declaration is:

typedef int64_t xsd__negativeInteger : -1 ;

Another possibility is to use strings to represent unbounded integers and do the translation in the application itself.

xsd:nonNegativeInteger

Corresponds to a non-negative unbounded integer. Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:

typedef uint64_t xsd__nonNegativeInteger 0 : ;

Another possibility is to use strings to represent unbounded integers and do the translation in the application itself.

xsd:nonPositiveInteger

Corresponds to a non-positive unbounded integer. Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:

typedef int64_t xsd__nonPositiveInteger : 0 ;

Another possibility is to use strings to represent unbounded integers and do the translation in code.

xsd:normalizedString

Represents normalized character strings. Normalized character strings do not contain the carriage return (#xD), line feed (#xA) nor tab (#x9) characters. It is recommended to use strings to store xsd:normalizedString XSD types. The type declaration is:

typedef char *xsd__normalizedString;

or

typedef std::string xsd__normalizedString;

xsd:positiveInteger

Corresponds to a positive unbounded integer. C/C++ does not support unbounded integers as a standard feature. The recommended type declaration is:

typedef uint64_t xsd__positiveInteger 1 : ;

Another possibility is to use strings to represent unbounded integers and do the translation in the application itself.

xsd:short

Corresponds to a 16-bit integer in the range -32768 to 32767. The type declaration is:

typedef short xsd__short;

xsd:string

Represents character strings. The type declaration is:

typedef char *xsd__string;

or

typedef std::string xsd__string;

The type declaration for wide character strings is:

typedef wchar_t *xsd__string;

or

typedef std::wstring xsd__string;

Both types of regular and wide strings can be used at the same time, by using a typedef name with a trailing underscore as follows:

typedef wchar_t *xsd__string_;

or

typedef std::wstring xsd__string_;

xsd:time

Represents a time. The lexical representation for time is the left truncated lexical representation for dateTime: hh:mm:ss.sss with optional following time zone indicator. It is recommended to use strings (char*) to store xsd:time XSD types. The type declaration is:

typedef char *xsd__time;

or

typedef std::string xsd__time;

Better is to use a custom serializer gsoap/custom/long_time.h to represent xsd:time or a string to avoid losing accuracy.

xsd:token

Represents tokenized strings. Tokens are strings that do not contain the line feed (#xA) nor tab (#x9) characters, that have no leading or trailing spaces (#x20) and that have no internal sequences of two or more spaces. It is recommended to use strings to store xsd:token XSD types. The type declaration is:

typedef char *xsd__token;

xsd:unsignedByte

Corresponds to an 8-bit unsigned integer in the range 0 to 255. The type declaration is:

typedef uint8_t xsd__unsignedByte;

xsd:unsignedInt

Corresponds to a 32-bit unsigned integer in the range 0 to 4294967295. The type declaration is:

typedef uint32_t xsd__unsignedInt;

xsd:unsignedLong

Corresponds to a 64-bit unsigned integer in the range 0 to 18446744073709551615. The type declaration is:

typedef uint64_t xsd__unsignedLong;

xsd:unsignedShort

Corresponds to a 16-bit unsigned integer in the range 0 to 65535. The type declaration is:

typedef uint16_t xsd__unsignedShort;

🔝 Back to table of contents

How to use multiple C/C++ types for a single primitive XSD type

As explained in Section C/C++ identifier name to XML tag name translation, trailing underscores in a type name are not relevant in XML and in the XML schemas generated by soapcpp2. Therefore, we can map multiple C/C++ types to XSD types (or any XML schema type). For example, the following declaration in the interface header file for soapcpp2 permits us to use regular strings and wide strings while mapping these both to the XSD xsd:string type:

typedef char *xsd__string;
typedef wchar_t *xsd__string_;

🔝 Back to table of contents

How to use C++ wrapper classes to specify polymorphic primitive types

XSD schema types form a hierarchy of types, with xsd:anyType at the root. A container or array of xsd:anyType may actually contain any mix of types, i.e. this container or array is polymorphic.

On the one hand, the typedef construct provides a convenient way to associate existing C/C++ types with XML schema types and makes it easy to incorporate these types in a (legacy) C/C++ application without having to replace application types in the source code. On the other hand the typedef declarations cannot be used to support polymorphic types.

To create a derivable primitive type T, a wrapper class is declared as follows:

class prefix__type_name : public xsd__super_type_name
{ public:
T __item;
... // other members, see note below
};

where T is a primitive C/C++ type. The __item member must be the first member of the wrapper class and all other members are not serialized.

For example, the a large portion of the XML type hierarchy can be implemented in C++ as follows:

class xsd__anyType { };
class xsd__anySimpleType : public xsd__anyType { };
typedef char *xsd__string;
class xsd__string_ : public xsd__anySimpleType { public: xsd__string __item; };
typedef xsd__string xsd__anyURI;
class xsd__anyURI_ : public xsd__anySimpleType { public: xsd__anyURI __item; };
typedef bool xsd__boolean;
class xsd__boolean_ : public xsd__anySimpleType { public: xsd__boolean __item; };
typedef xsd__string xsd__date;
class xsd__date_ : public xsd__anySimpleType { public: xsd__date __item; };
typedef time_t xsd__dateTime;
class xsd__dateTime_ : public xsd__anySimpleType { public: xsd__dateTime __item; };
typedef double xsd__double;
class xsd__double_ : public xsd__anySimpleType { public: xsd__double __item; };
typedef xsd__string xsd__duration;
class xsd__duration_ : public xsd__anySimpleType { public: xsd__duration __item; };
typedef float xsd__float;
class xsd__float_ : public xsd__anySimpleType { public: xsd__float __item; };
typedef xsd__string xsd__time;
class xsd__time_ : public xsd__anySimpleType { public: xsd__time __item; };
typedef xsd__string xsd__decimal;
class xsd__decimal_ : public xsd__anySimpleType { public: xsd__decimal __item; };
typedef xsd__string xsd__integer;
class xsd__integer_ : public xsd__decimal_ { public: xsd__integer __item; };
typedef LONG64 xsd__long;
class xsd__long_ : public xsd__integer_ { public: xsd__long __item; };
typedef long xsd__int;
class xsd__int_ : public xsd__long_ { public: xsd__int __item; };
typedef short xsd__short;
class xsd__short_ : public xsd__int_ { public: xsd__short __item; };
typedef char xsd__byte;
class xsd__byte_ : public xsd__short_ { public: xsd__byte __item; };
typedef xsd__string xsd__nonPositiveInteger;
class xsd__nonPositiveInteger_ : public xsd__integer_ { public: xsd__nonPositiveInteger __item; };
typedef xsd__string xsd__negativeInteger;
class xsd__negativeInteger_ : public xsd__nonPositiveInteger_ { public: xsd__negativeInteger __item; };
typedef xsd__string xsd__nonNegativeInteger;
class xsd__nonNegativeInteger_ : public xsd__integer_ { public: xsd__nonNegativeInteger __item; };
typedef xsd__string xsd__positiveInteger;
class xsd__positiveInteger_ : public xsd__nonNegativeInteger_ { public: xsd__positiveInteger __item; };
typedef ULONG64 xsd__unsignedLong;
class xsd__unsignedLong_ : public xsd__nonNegativeInteger_ { public: xsd__unsignedLong __item; };
typedef unsigned long xsd__unsignedInt;
class xsd__unsignedInt_ : public xsd__unsignedLong_ { public: xsd__unsignedInt __item; };
typedef unsigned short xsd__unsignedShort;
class xsd__unsignedShort_ : public xsd__unsignedInt_ { public: xsd__unsignedShort __item; };
typedef unsigned char xsd__unsignedByte;
class xsd__unsignedByte_ : public xsd__unsignedShort_ { public: xsd__unsignedByte __item; };
typedef xsd__string xsd__normalizedString;
class xsd__normalizedString_ : public xsd__string_ { public: xsd__normalizedString __item; };
typedef xsd__string xsd__token;
class xsd__token_ : public xsd__normalizedString_ { public: xsd__token __item; };

Note the use of the trailing underscores for the class names to distinguish the typedef type names from the class names. The char* type of xsd__string can be replaced with std::string or a wide string type. We can also add the xsd:base64Binary and xsd:hexBinary types that serialize raw binary data in the hierarchy as follows:

class xsd__base64Binary : public xsd__anySimpleType { public: unsigned char *__ptr; int __size; };
class xsd__hexBinary : public xsd__anySimpleType { public: unsigned char *__ptr; int __size; };

See Sections base64Binary serialization and hexBinary serialization .

Methods can be added to these classes, such as constructors and getter/setter methods, see Section Get and set methods .

Wrapper structs are supported as well, similar to wrapper classes. But they cannot be used to implement polymorphism. Rather, the wrapper structs are used to represent a xsd:sequence of elements or to add attributes to primitive types as explained in Section How to declare XML attributes .

For additional details, see the C and C++ XML Data Bindings documentation.

🔝 Back to table of contents

Multi-reference strings

If more than one char pointer points to the same string, the string is encoded as a multi-reference value, unless SOAP_XML_TREE is used or the WITH_NOIDREF compile-time flag.

Consider for example:

class ns__record
{ public:
const char *s;
const char *t;
};

A record instance is populated as follows and then serialized:

struct soap *soap = soap_new1(SOAP_XML_GRAPH);
ns__record record;
record.s = "hello";
record.t = s;
soap_write_ns__record(soap, &record);

The s and t variables are assigned the same string. When serialized, t refers to the content of s:

1 <ns:record>
2  <s id="_1">hello</s>
3  <t ref="_1"/>
4 </ns:record>

However, strings declared with different typedef names will never be considered multi-reference even when they point to the same string. For example:

typedef char *xsd__string;
typedef char *ns__string;
class ns__record
{ public:
const xsd__string s;
const ns__string t;
};

This avoids type conflicts when a receiver considers these types incompatible.

To enable multi-references in XML use SOAP_XML_GRAPH. To disable multi-references in SOAP 1.1 and 1.2 RPC encoded messages, use SOAP_XML_TREE.

🔝 Back to table of contents

Smart string mixed-content deserialization

The implementation of string deserialization permits mixed content. When XML contains mixed text and tags when a string is expected, the text with tags are collected into the deserialized string.

For example, suppose the getInfo service operation returns some detailed information. The service operation is declared as:

// Contents of file "getInfo.h":
getInfo(char *detail);

The proxy of the service is used by a client to request a piece of information and the service responds with:

HTTP/1.1 200 OK 
Content-Type: text/xml 
Content-Length: nnn 
1 <SOAP-ENV:Envelope
2  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
3  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
4  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
6  <SOAP-ENV:Body>
7  <getInfoResponse>
8  <detail>
9  <picture>Mona Lisa by <i>Leonardo da Vinci</i></picture>
10  </detail>
11  </getInfoResponse>
12  </SOAP-ENV:Body>
13 </SOAP-ENV:Envelope>

The detail string will contain "\<picture\>Mona Lisa by \<i\>Leonardo da Vinci\</i\>\</picture\>".

Note that serialization of this string will not produce mixed content but rather the XML output:

1 Mona Lisa by &lt;i&gt;Leonardo da Vinci&lt;/i&gt;

To serialize XML stored in strings, use the _XML type (a char*) in the interface header file for soapcpp2. For example:

// Contents of file "getInfo.h":
getInfo(_XML detail);

In C++ you can use a std::string instead, as follows:

// Contents of file "getInfo.h":
typedef std::string XML;
getInfo(XML detail);

The _XML and typedef XML are literal XML strings, see also Section Serializing mixed content with literal XML strings.

🔝 Back to table of contents

Changing the precision of float and double types

The format used to output double precision floating point values in XML is by default set to "`%.17lG`", which means that at most 17 digits of precision are output. The format used by the gSOAP engine to output single precision floating point values is by default "`%.9G`".

The format of a double can be set by assigning a format string to soap::double_format. For example:

struct soap soap;
soap_init(&soap); // sets double_format = "%.18G"
soap.double_format = "%e"; // redefine

which causes all doubles to be output in XML and JSON in scientific notation. Likewise, the encoding format of a float type can be set by assigning a format string to the soap::float_format string variable. For example:

struct soap soap;
soap_init(&soap); // sets float_format = "%.9G"
soap.float_format = "%.4f"; // redefine

which causes all floats to be output in XML and JSON with four digits precision.

A new feature to specify format patterns was introduced in gSOAP 2.8.18. A format string can be used as a pattern for a typedef float or double in the interface header file for soapcpp2 to specify the representation in XML. For example:

typedef float time__ratio "%5.2f";

This will output the float in XML with 5 digits total and 2 digits after the decimal point.

The soapcpp2 tool also generates an XML schema with xsd:totalDigits and xsd:fractionDigits for this type:

1 <simpleType name="ratio">
2  <restriction base="xsd:float">
3  <totalDigits value="5"/>
4  <fractionDigits value="2"/>
5  </restriction>
6 </simpleType>

The wsdl2h tool converts WSDLs and XSDs with xsd:totalDigits and xsd:fractionDigits to typedefs with format patterns.

🔝 Back to table of contents

INF, -INF, and NaN values of float and double types

IEEE INF, -INF, and NaN values of floats are output in XML as INF, -INF, and NaN, respectively, as supported by the XML schema standards.

For portability, the following macros can be used containing the float and double values INF, -INF, and NaN:

float x = FLT_PINFTY;
float x = FLT_NINFTY;
float x = FLT_NAN;
double x = DBL_PINFTY;
double x = DBL_NINFT;
double x = DBL_NAN;

To check for INF, -INF, and NaN use:

soap_isinf(x) && x > 0 // x is INF
soap_isinf(x) && x < 0 // x is -INF
soap_isnan(x) // x is NaN

🔝 Back to table of contents

Enumeration serialization

Enumerations are generally useful for the declaration of named integer-valued constants.

For additional details, see the C and C++ XML Data Bindings documentation.

🔝 Back to table of contents

Serialization of symbolic enumeration constants

The soapcpp2 tool encodes the constants of enumeration-typed variables in symbolic form using the names of the constants when possible to comply to SOAP's enumeration encoding style. Consider for example the following enumeration of weekdays:

enum weekday { Mon, Tue, Wed, Thu, Fri, Sat, Sun };

The enumeration-constant Mon, for example, is encoded as

1 <weekday>Mon</weekday>

An XML namespace prefix can be specified as part of the enumeration-type identifier's name, with the usual namespace prefix conventions for identifiers. For example:

enum ns__weekday { Mon, Tue, Wed, Thu, Fri, Sat, Sun };

The ns__weekday type with enumeration-constant Sat, for example, is output in XML as:

1 <ns:weekday>Sat</ns:weekday>

The corresponding XML schema type for this enumeration type is:

1 <xsd:simpleType name="weekday">
2  <xsd:restriction base="xsd:string">
3  <xsd:enumeration value="Mon"/>
4  <xsd:enumeration value="Tue"/>
5  <xsd:enumeration value="Wed"/>
6  <xsd:enumeration value="Thu"/>
7  <xsd:enumeration value="Fri"/>
8  <xsd:enumeration value="Sat"/>
9  <xsd:enumeration value="Sun"/>
10  </xsd:restriction>
11 </xsd:simpleType>

C++11 scoped enumerations are supported by soapcpp2 with option -c++11:

enum class ns__weekday : int { Mon, Tue, Wed, Thu, Fri, Sat, Sun };

Enumeration constants can be initialized, for example:

enum ns__relation { LESS = -1, EQUAL = 0, GREATER = 1 };

The symbolic names LESS, EQUAL, and GREATER will appear in the XML output.

If the value of an enumeration-typed variable has no corresponding named constant, the value is encoded as a signed integer literal. For example, the following declaration of a workday enumeration type lacks named constants for Saturday and Sunday:

enum ns__workday { Mon, Tue, Wed, Thu, Fri };

If the constant 5 (Saturday) or 6 (Sunday) is assigned to a variable of the workday enumeration type, the variable will be encoded with the integer literals 5 and 6, respectively. For example:

1 <ns:workday>5</ns:workday>

Since this is legal in C/C++ and in SOAP RPC encoding, but not XML validators, we cam transmit integer literals as well as enumeration constants with an enumeration type.

When enumeration constants are numeric, we can use the following simple trick:

enum ns__nums { _1 = 1, _2 = 2, _3 = 3 };

The corresponding XML schema type for this enumeration type is:

1 <simpleType name="nums">
2  <restriction base="xsd:long">
3  <enumeration value="1"/>
4  <enumeration value="2"/>
5  <enumeration value="3"/>
6  </restriction>
7 </simpleType>

🔝 Back to table of contents

How to reuse symbolic enumeration constants

A well-known deficiency of C and C++ enumeration types before C++11 scoped enumerations is the lack of a mechanism to reuse symbolic names by multiple enumerations. This issue is largely resolved with scoped enumerations in C++11, which the soapcpp2 tool supports.

In plain C and C++ we can use trailing underscores to avoid name clashes, for example:

Consider for example:

enum ns__workday { Mon, Tue, Wed, Thu, Fri };
enum ns__weekday { Mon_, Tue_, Wed_, Thu_, Fri_, Sat_, Sun_ };

which will result in the encoding of the constants of enum ns__weekday without the underscore, for example as Mon.

However, the soapcpp2 tool is a bit smarter than your average C/C++ compiler and also permits the following declarations that reuse enumeration constants, because the enumeration constants have the same enumerating integer values:

enum ns__workday { Mon, Tue, Wed, Thu, Fri };
enum ns__weekday { Mon, Tue, Wed, Thu, Fri, Sat, Sun };

The soapcpp2 tool generates soapStub.h with amended enumeration definitions that the C/C++ compiler can handle, so you can still use the shared enumeration constants in your application code.

To avoid name clashes with enumeration constants, you can use the following convention with double underscores to add the enum name to the enum constants:

enum prefix__name { prefix__name__enumconst1, prefix__name__enumconst2, ... };

where the type name of the enumeration prefix__name is a prefixed name, such as:

enum ns__workday {
ns__workday__Mon,
ns__workday__Tue,
ns__workday__Wed,
ns__workday__Thu,
ns__workday__Fri
};
enum ns__weekday {
ns__workday__Mon,
ns__workday__Tue,
ns__workday__Wed,
ns__workday__Thu,
ns__workday__Fri,
ns__workday__Sat,
ns__workday__Sun
};

This ensures that the XML schema enumeration values are still simply Mon, Tue, Wed, Thu, Fri, Sat, and Sun.

Warning
The following declaration:
enum ns__workday { Mon, Tue, Wed, Thu, Fri };
enum ns__weekday { Sat = 5, Sun = 6};
will not properly encode the weekday enumeration when you assume that workdays are part of weekdays, because it lacks the named constants for workday in its enumeration list. All enumerations must be self-contained and cannot use enum constants of other enumerations.

🔝 Back to table of contents

Boolean enumeration serialization for C

The C++ bool type that is serialized as xsd:boolean XSD type cannot be used in C. Instead, an enumeration type should be used to serialize true and false values as xsd:boolean XSD type values. The xsd:boolean XSD type is defined as an enumeration in C as:

enum xsd__boolean { false_, true_ };

The value false_, for example, is output in XML as:

1 <xsd:boolean>false</xsd:boolean>

Peculiar of the SOAP encoding boolean type is that it only defines the values 0 and 1, while the XSD xsd:boolean type defines false and true as valid values. While SOAP encoding types are rarely used since almost all SOAP/XML Web services rely on XSD types for primitive values, we can still define the following:

typedef int SOAP_ENC__boolean;

🔝 Back to table of contents

Bitmask enumeration serialization

A bitmask is an enumeration of power-of-two flags. The soapcpp2 tool makes it easy to define bitmasks using an annotated enum with a *:

enum * name { enum-constant, enum-constant, ... };

This declares a regular enum but enumerates the enumeration constants as a series of powers of 2 starting with 1. This means that the enumeration constants can be bitwise or-ed with the | operator to form a bitvector (bitmask) which is serialized in XML as a list of symbolic values. For example:

enum * ns__machineStatus { ON, BELT, VALVE, HATCH};
int ns__setMachineStatus(enum ns__machineStatus status, enum ns__machineStatus *result);

Note that the use of the enum name as a parameter does not require the asterisk, only the definition does. The soapcpp2 tool generates a proper C/C++ enumeration in soapStub.h that is included by soapH.h by your application:

enum ns__machineStatus { ON=1, BELT=2, VALVE=4, HATCH=8 };

The corresponding XML schema type for this enumeration type is:

1 <simpleType name="machineStatus">
2  <list>
3  <restriction base="xsd:string">
4  <enumeration value="ON"/>
5  <enumeration value="BELT"/>
6  <enumeration value="VALVE"/>
7  <enumeration value="HATCH"/>
8  </restriction>
9  </list>
10 </simpleType>

The values of enum ns__machineStatus can be or-ed, for example ON|VALVE is output in XML as:

1 <ns:machineStatus>ON VALVE</ns:machineStatus>

C++11 scoped enumerations for bitmasks are supported by soapcpp2, for example:

enum * class ns__machineStatus { ON, BELT, VALVE, HATCH};
int ns__setMachineStatus(ns__machineStatus status, ns__machineStatus *result);

🔝 Back to table of contents

Struct and class serialization

This section gives a brief overview of struct and class serialization.

Structs do not support inheritance when declared in an interface header file for soapcpp2. This makes serialization of structs is more efficient compared to classes. Serialization functions for structs are global functions. By contrast, soapcpp2 augments classes with serialization methods and soap_type() method that returns the type of the class instance, which is necessary to distinguish base class instances from derived class instances for (smart) pointers to base class instances.

For additional details not covered here, see the C and C++ XML Data Bindings documentation.

A class and struct instance is serialized as an XML element with attributes and sub-elements, which is represented in XML schema as a complexType. The class name is the XML schema type name and the member variables of the class are the type's accessors.

Consider the general declaration of an inherited class:

class prefix__class_name1 : public prefix__class_name2
{ public:
field1;
field2;
... // more fields
method1;
method2;
... // more methods
};

then

  • prefix__ is the optional namespace prefix associated with the class.
  • class_name1 is the name of the complexType for this class.
  • class_name2 is an optional base class.
  • field is a member variable that is serialized when public and non-const.
  • method is a method declaration. Abstract methods are not allowed for serializable classes.

A class name is required to be unique and cannot have the same name as a struct, enum, or a service operation name specified in the interface header file for soapcpp2.

Only single inheritance is supported by the soapcpp2 tool. Multiple inheritance is not supported because of the limitations of the XML schema extensibility.

If a constructor is present, there must also be a constructor declaration with an empty parameter list. If no constructors are present, then soapcpp2 generates constructors to initialize the members with the generated soap_default method of this class.

To obtain more information about the code generated by soapcpp2 for a struct or class, use soapcpp2 -r option -r to generate a soapReadme.md report with all the details.

Classes and structs may be declared volatile if you don't want soapcpp2 to generate the class definition, see Section Serialization "as is" of volatile data types for more details.

Class templates are supported with only one template argument, see Section Containers .

Member variables of a class can be serialized as XML attributes using the @ type qualifier, if the member is a primitive type or pointer to a primitive type. See Section How to declare XML attributes for more details.

See Section C/C++ identifier name to XML tag name translation for more details on the struct/class member serialization and the resulting element and attribute qualified forms.

Arrays may be embedded within a class and a struct using a pointer member and size information, see Section Non-SOAP dynamic arrays .

Void pointers may be used in a class or a struct, but you have to add a type field so the engine can determine the type of object pointed to, see Section Void pointer serialization .

A class instance is output in XML as:

1 <prefix:class-name>
2  <basefield1>...</basefield1>
3  <basefield2>...</basefield2>
4  ...
5  <field1>...</field1>
6  <field2>...</field2>
7  ...
8 </prefix:class-name>

where the field accessors have element-name representations of the class members and the basefield accessors have element-name representations of the base class members.

If a derived class instance is used in place of a base class instance, then the serialized XML form carries a xsi:type attribute with the derived class type to distinguish it from the base class type:

1 <prefix:class-name xsi:type="prefix:class-name">
2  <basefield1>...</basefield1>
3  <basefield2>...</basefield2>
4  ...
5  <field1>...</field1>
6  <field2>...</field2>
7  ...
8 </prefix:class-name>

The deserialization of a class instance allows any ordering of the accessors in the XML message. However, if a base class member name is identical to a derived class member name, because the member is overloaded, the base class member name must precede the derived class member name in the XML message.

For additional details, see the C and C++ XML Data Bindings documentation.

🔝 Back to table of contents

Example

The following example declares a base class ns__Object and a derived class ns__Shape:

// Contents of file "shape.h":
class ns__Object
{ public:
@ char *name;
};
class ns__Shape : public ns__Object
{ public:
@ int sides;
@ enum ns__Color { Red, Green, Blue } color;
std::string description;
ns__Shape();
ns__Shape(int sides, enum ns__Color color, std::string& description);
~ns__Shape();
};

The implementation of the class ns__Shape methods cannot be part of the interface header file for soapcpp2 and are defined in a separate shape.cpp C++ source code file.

An instance of class ns__Shape with name Triangle, 3 sides, and color Green is output in XML as:

1 <ns:Shape name="Triangle" sides="3" color="Green">
2  <description>This is a green triangle</description>
3 </ns:shape>

🔝 Back to table of contents

Class methods

A class declaration in the interface header file for soapcpp2 may include method declarations. The method implementations must not be part of the header file but should be defined in another C++ source file, because soapcpp2 parses C/C++ type declarations but does not parse C/C++ code statements and constructor initializer lists.

If constructors are not defined, then soapcpp2 generates constructors for the class to initialize the class with default values for member variables or the initialization values for member variables given in the class declaration.

If destructors are not defined, then soapcpp2 generates destructors for the class.

To obtain more information about the code generated by soapcpp2 for a class, use soapcpp2 -r option -r to generate a soapReadme.md report with all the details.

🔝 Back to table of contents

Get and set methods

Setter and getter methods are invoked at run time upon serialization and deserialization of class instances, respectively. The use of setter and getter methods adds more flexibility to the serialization and deserialization process.

A setter method is called by the serializer. You can use setter methods to update a class instance just before it is serialized. For example, you can use setter methods to update a class instance right before serialization. Setters are methods for "set to serialize" operations.

Getter methods are immediately invoked after deserialization of a class instance. You can use them to adjust the contents of class instances right after the instance was populated by the deserializer

Getter and setter methods have the following class method signature:

int get(struct soap *soap);
int set(struct soap *soap);

These methods may be declared virtual and may be declared const.

The active soap context will be passed to the get and set methods. The methods should return SOAP_OK when successful. A setter method should prepare the contents of the class instance for serialization. A getter method should process the instance after deserialization.

Here is an example of a base64 binary class:

class xsd__base64Binary
{ public:
unsigned char *__ptr;
int __size;
int get(struct soap *soap);
int set(struct soap *soap);
};

Suppose that the type and options members of the attachment should be set when the class is about to be serialized. This can be accomplished with the set method from the information provided by the __ptr to the data and the soap context passed to the set method (you can pass data via the void* soap::user member).

The get method is invoked after the base64 data has been processed. You can use it for post-processing purposes.

Here is another example. It defines a primitive update type. The class is a wrapper for the time_t type, see Section How to use C++ wrapper classes to specify polymorphic primitive types . Therefore, elements of this type contain xsd:dateType data.

class update
{ public:
time_t __item;
int set(struct soap *soap);
};

The setter method assigns the current time just before the instance is serialized:

int update::set(struct soap *soap)
{
this->__item = time(NULL);
return SOAP_OK;
}

This means that serialization in XML results in the inclusion of an up-to-date time stamp.

A get method is invoked immediately after the instance is populated by the deserializer. The method is not invoked when the element is an xsi:nil element or has a SOAP href or ref attribute referencing a value located elsewhere in the XML message or document.

Note
The soap_out method of a class calls the setter method However, the soap_out method is declared const while the setter should be allowed to modify the contents of the class instance. Therefore, the soapcpp2-generated code recasts the instance and the const is removed when invoking the setter.

🔝 Back to table of contents

Updating and checking instances with get and set methods

Getter methods enable streaming XML operations. A getter method is invoked when the object is deserialized and the rest of the XML message has not been parsed yet. For example, you can add a getter method to the SOAP Header class to implement header processing logic that is activated as soon as the SOAP Header is received. An example is shown below:

class h__Authentication
{ public:
char *id;
int get(struct soap *soap);
};
{ public:
h__Authentication *h__authentication;
};

The Authentication SOAP Header member is instantiated and decoded. After decoding, the getter method is invoked, which can be used to check the id before the rest of the SOAP message is parsed.

🔝 Back to table of contents

Polymorphism, derived types, and dynamic binding in C++

Polymorphism through C++ inheritance is supported by the gSOAP tools, which means that derived XML schema types are (de)serialized when an xsi:type attribute is present.

Because C does not support inheritance, a different approach is use for C code, see Section Polymorphism, derived types, and dynamic binding in C for details.

Base and derived C++ classes can be used anywhere, including service operation parameters and in struct and class members, provided that parameters and members are pointers to classes to allow dynamic binding at run time. Base and derived classes can also be used with containers such as std::vector and smart pointers such as std::shared_ptr.

The following example interface header file for soapcpp2 declares ns__Base and ns__Derived classes and a service operation that takes a pointer to a ns__Base class instance and returns a ns__Base class instance:

// Contents of file "derived.h"
class ns__Base
{ public:
char *name;
ns__Base();
virtual void print();
};
class ns__Derived : public ns__Base
{ public:
int num;
ns__Derived();
virtual void print();
};
int ns__webmethod(ns__Base *in, struct ns__webmethodResponse { ns__Base *out; } & result);

The service operation input parameter may point to a ns__Derived class instance that will be serialized as ns__Derived class instance instead of a ns__Base class instance. Likewise, the service operation output parameter that is placed in a wrapper struct (because structs and classes are always considered wrappers to define the response message with output parameters) may point to a ns__Derived class instance.

The ns__Base and ns__Derived class method implementations are:

// Method implementations of the ns__Base and ns__Derived classes:
#include "soapH.h"
ns__Base::ns__Base()
{
std::cout << "created a Base class instance" << std::endl;
}
ns__Derived::ns__Derived()
{
std::cout << "created a Derived class instance" << std::endl;
}
ns__Base::print()
{
std::cout << "print(): Base class instance " << name << std::endl;
}
ns__Derived::print()
{
std::cout << "print(): Derived class instance " << name << " " << num << std::endl;
}

Below is an example client application that creates a ns__Derived class instance that is passed as the input parameter of the ns__webmethod service operation:

// CLIENT
#include "soapH.h"
int main()
{
struct soap soap;
soap_init(&soap);
ns__Derived obj;
struct ns__webmethodResponse r;
soap_default_ns__Derived(&soap, &obj);
obj.name = "X";
obj.num = 3;
if (soap_call_ns__webmethod(&soap, endpoint, NULL, &obj, r) == SOAP_OK)
if (r.out)
r.out->print();
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
}

The following example server application copies a class instance (ns__Base or ns__Derived) from the input to the output parameter:

// SERVER
#include "soapH.h"
int main()
{
struct soap soap;
soap_init(&soap);
soap_serve(&soap);
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
}
int ns__webmethod(struct soap *soap, ns__Base *in, struct ns__webmethodResponse &result)
{
if (in)
in->print();
result.out = in;
return SOAP_OK;
}

The following messages are produced by the client and server applications:

CLIENT: created a Derived class instance 
SERVER: print(): Derived class instance X 3 
CLIENT: created a Derived class instance 
CLIENT: print(): Derived class instance X 3

This shows that the Derived class instance kept its identity as it passed through the server.

Another way to serialize polymorphic values in XML that are indicated with xsi:type attributes is with void* members that point to a serializable value. See Section Void pointer serialization for details.

🔝 Back to table of contents

Polymorphism, derived types, and dynamic binding in C

Because C does not support object-oriented inheritance, derived types are obviously not declared as base structs or classes as in C++. Instead, we add the derived type structs to the base structs as members that point to the derived type value when the base type is dynamically overridden by one of the derived types. In this way we can (de)serialize a base type struct as usual or one of the derived structs when the base type is overridden. To serialize a derived type struct in place of the base struct, we set its corresponding member point to the derived struct value, which is serialized with the xsi:type attribute to indicate a derived type is used in XML. Deserialization of a derived type struct is done automatically when the xsi:type attribute is present.

This approach with additional members pointing to derived types was introduced with gSOAP 2.8.75. This approach has the benefit of type safety compared to attempts to replicate C++ inheritance by trying to overlay derived types with base types in memory, which would be fragile.

This method is fully automated for the wsdl2h tool to generate an interface header file for soapcpp2 with the type derivations in C. To use this method to generate code from WSDLs and XSDs, use wsdl2h -F option -F. This also works in C++, but C++ inheritance works fine without this method.

Using this method with soapcpp2 alone using a manually-specified interface header file produces the specified type inheritance in the soapcpp2-generated WSDL and XML schema files as complexType extensions.

The soapcpp2 tool warns if a derived type has multiple base types. At most one base type for a derived type may be specified.

To illustrate this method, consider the following interface header file example for soapcpp2 based on Polymorphism, derived types, and dynamic binding in C++. This example declares ns__Base and ns__Derived structs and a service operation that takes a pointer to a ns__Base value and returns a ns__Base value:

// Contents of file "derived.h"
struct ns__Base
{
char *name;
[ struct ns__Derived *ns__Derived; ] // points to derived type when non-NULL
};
struct ns__Derived
{
char *name;
int num;
};
int ns__webmethod(struct ns__Base *in, struct ns__webmethodResponse { struct ns__Base *out; } *result);

The ns__Base struct includes the special member ns__Derived that points to a ns__Derived value. This special member must be:

  • a transient member (i.e. non-serializable) by placing the declaration within [ and ], and
  • the member name must match the type name (to be more precise, at least the initial part of the member name must match the type name as in the example ns__Derived_ works too).

To serialize the ns__Base value make sure to set the ns__Derived member to NULL. The soapcpp2-generated soap_default_ns__Base() function default initializes a given ns__Base value for you. To serialize the ns__Derived value make sure to set the ns__Derived member to point to the address of a ns__Derived value. This is easy by calling soap_new_ns__Derived() that allocates and default initializes a ns__Derived value, whose address is returned by this function.

When multiple derived types are declared for a base type, all immediately derived struct types are added as transient pointer members to the base type. Indirectly derived types do not need to be added to the base type as members, but it is perfectly fine to do so.

To properly declare derived types, make sure to include all base type members in the derived type. In our example the ns__Derived struct contains the ns__Base struct members (except for the ns__Derived member) and adds additional members as extensions.

Below is an example client application based on the example in Section Polymorphism, derived types, and dynamic binding in C++ that creates a ns__Derived value that is passed as the input parameter of the ns__webmethod service operation:

// CLIENT
#include "soapH.h"
int main()
{
struct soap soap;
soap_init(&soap);
struct ns__Base obj;
struct ns__Derived der;
struct ns__webmethodResponse r;
soap_default_ns__Base(&soap, &obj);
soap_default_ns__Derived(&soap, &der);
obj.ns__Derived = &der;
der.name = "X";
der.num = 3;
if (soap_call_ns__webmethod(&soap, endpoint, NULL, &obj, &r) == SOAP_OK)
{
if (r->out && r.out->ns__Derived)
printf("print(): Derived class instance %s %d\n",
r.out->ns__Derived->name,
r.out->ns__Derived->num);
else if (r->out)
printf("print(): Base class instance %s\n",
r.out->name);
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
}

The following example server application copies a class instance (ns__Base or ns__Derived class) from the input to the output parameter:

// SERVER
#include "soapH.h"
int main()
{
struct soap soap;
soap_init(&soap);
soap_serve(&soap);
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
}
int ns__webmethod(struct soap *soap, struct ns__Base *in, struct ns__webmethodResponse *result)
{
if (in && in->ns__Derived)
printf("print(): Derived class instance %s %d\n",
in->ns__Derived->name,
in->ns__Derived->num);
else if (in)
printf("print(): ns__Base class instance %s\n",
in->name);
result.out = in;
return SOAP_OK;
}

The following messages are produced by the client and server applications:

SERVER: print(): Derived class instance X 3 
CLIENT: print(): Derived class instance X 3

This shows that the Derived class instance kept its identity as it passed through the server.

Another way to serialize polymorphic values in XML that are indicated with xsi:type attributes is with void* members that point to a serializable value. See Void pointer serialization for details.

Deeper levels of simulated inheritance are possible, for example:

// Contents of file "derived.h"
struct ns__Base
{
char *name;
[ struct ns__Derived *ns__Derived; ] // points to derived type when non-NULL
};
struct ns__Derived
{
char *name;
int num;
[ struct ns__Derived2 *ns__Derived2; ] // points to derived type when non-NULL
};
struct ns__Derived2
{
char *name;
int num;
char *value;
};
int ns__webmethod(struct ns__Base *in, struct ns__webmethodResponse { struct ns__Base *out; } *result);

This requires two pointer traversals from the base type ns__Base via ns__Derived to reach ns__Derived2:

int ns__webmethod(struct soap *soap, struct ns__Base *in, struct ns__webmethodResponse *result)
{
if (in && in->ns__Derived && in->ns__Derived->ns__Derived2)
printf("print(): Derived2 class instance %s %d %s\n",
in->ns__Derived->ns__Derived2->name,
in->ns__Derived->ns__Derived2->num,
in->ns__Derived->ns__Derived2->value);
else if (in && in->ns__Derived)
printf("print(): Derived class instance %s %d\n",
in->ns__Derived->name,
in->ns__Derived->num);
else if (in)
printf("print(): ns__Base class instance %s\n",
in->name);
result.out = in;
return SOAP_OK;
}

🔝 Back to table of contents

How to declare XML attributes

The gSOAP tools support the full XML schema standards, so XML attributes are nothing special. However, with respect to SOAP standards it is important to note that SOAP RPC/literal and SOAP document/literal styles support XML attributes in SOAP messages, but SOAP RPC with "Section 5" encoding does not support XML attributes other than some built-in attributes.

The idea behind SOAP RPC Section 5 encoding was to keep SOAP as simple as possible as a limited subset of XML, while offering advantages for cross-language interoperability of data types, including data structure graph serialization with multi-referenced data.

Attributes are primitive XSD types, such as strings, enumerations, boolean, and numeric types. To declare an XML attribute in a struct or class, the qualifier @ is used with the type of the attribute. The type must be primitive type or a pointer to a primitive type, including enumerations and xsd__base64Binary and xsd__hexBinary structures. For example:

typedef char *xsd__string;
typedef bool *xsd__boolean;
enum ns__state { _0, _1, _2 };
struct ns__myStruct
{
@ std::string *type;
@ bool flag = false;
@ enum ns__state state = _2;
struct ns__myStruct *next;
};

The @ qualifier declares an XML attribute for the type, flag, and state members.

Default values can be associated with any member that has a primitive type in a struct or class, as is illustrated in this example. The default values are used when the receiving message does not contain the corresponding values.

Pointers make the members optional. So type is an optional attribute.

Because a service operation request and response message is essentially a struct, XML attributes can also be associated with method requests and responses. For example:

int ns__webmethod(@ char *ns__name, ...);

Attributes can also be attached to the dynamic arrays, binary types, and wrapper classes and structs of primitive types. Wrapper classes are described in Section How to use C++ wrapper classes to specify polymorphic primitive types . For example:

class xsd__string
{ public:
char * __item;
@ bool flag;
};

and

class xsd__base64Binary
{ public:
unsigned char *__ptr;
int __size;
@ bool flag;
};

The attribute declarations must be placed after the special __item, __ptr, and __size members.

For additional details, see the C and C++ XML Data Bindings documentation.

🔝 Back to table of contents

How to use QName attributes and elements

An element or attribute with type QName (Qualified Name) contains a namespace prefix and a local name. We can explicitly declare a QName as a string type with typedef char *xsd__QName and the serializer recognizes the QName type as a special type that requires QName normalization. A built-in QName type _QName is recognized by soapcpp2, which is a char* type with QName content.

QName normalization by the deserializer is applied to convert the prefix in the inbound XML message to the corresponding prefix defined in the XML namespace table, which means that the QName string is always received in normalized form.

For example:

//gsoap ns schema namespace: urn:example
typedef char *xsd__QName;
struct ns__myStruct
{
xsd__QName elt = "ns:xyz"; // QName element with default value "ns:xyz"
@ xsd__QName att = "ns:abc"; // QName attribute with default value "ns:abc"
};

When the elt and att members are serialized, their string contents are just output. When the members are deserialized however, the deserializer converts the prefix in the parsed QName to the prefix defined in the namespace table that corresponds to the same namespace URI. For example, suppose that the inbound XML message contains <elt xmlns:x="urn:example">x:def</elt>. The prefix x matches the namespace URI urn:example of prefix ns as declared by the //gsoap ns schema namespace: urn:example directive, which populates the namespace table ns.nsmap generated by soapcpp2. Therefore, the x:def QName value is converted to ns:def and saved in the elt member of ns__myStruct.

If the namespace URI used in the inbound XML message is not in the namespace table, for example when <elt xmlns:x="urn:x">x:def</elt> is parsed, then x:def is converted to "URI":def where "URI" is the namespace URI bound to x, which is "urn:x" in this case. This value "urn:x":defis saved in the elt member of ns__myStruct.

For additional details, see the C and C++ XML Data Bindings documentation.

🔝 Back to table of contents

Union serialization

A union is only serialized if the union is used within a struct or class declaration that includes an int __union member that acts as a selector (also called discriminant) for the union members. The selector stores run-time usage information about the union member that is activated.

A union within a struct or class with a selector member represents xsd:choice XML schema component. For example:

struct ns__PO
{
... // members of ns__PO
};
struct ns__Invoice
{
... // members of ns__Invoice
};
union ns__PO_or_Invoice
{
struct ns__PO po;
struct ns__Invoice invoice;
};
struct ns__composite
{
char *name;
int __union;
union ns__PO_or_Invoice value;
};

The union ns__PO_or_Invoice appears as a xsd:choice in the generated XML schema:

1 <complexType name="composite">
2  <sequence>
3  <element name="name" type="xsd:string"/>
4  <choice>
5  <element name="po" type="ns:PO"/>
6  <element name="invoice" type="ns:Invoice"/>
7  </choice>
8  </sequence>
9 </complexType>

The union name should be qualified, as shown in the example, to ensure correct serialization when the XML schemas is declared with elementFormDefault="qualified", with //gsoap ns schema elementForm: qualified.

The int __union member selector's values are generated by the soapcpp2 tool. Each union member name has a selector value defined by:

SOAP_UNION_unionname_fieldname

These selector values enumerate the union members. The special value 0 (or any negative value) can be assigned to omit the serialization of the union altogether, but only if explicitly allowed by validation rules, which requires minOccurs="0" for the xsd:choice:

struct ns__composite
{
char *name;
int __union 0; // declares <choice minOccurs="0">
union ns__PO_or_Invoice value;
};

This way we can treat the union as an optional data item by setting __union = 0.

Since 2.7.16 it is also possible to use a '$' as a special marker to annotate a selector member that must be of type int and the member name can be chosen arbitrarily:

struct ns__composite
{
char *name;
$ int select 0; // declares <choice minOccurs="0">
union ns__PO_or_Invoice value;
};

The following example shows how the struct ns__composite instance is initialized for serialization using the above declaration:

struct ns__composite data;
data.name = "...";
data.select = SOAP_UNION_ns__PO_or_Invoice_po; // select ns__PO_or_Invoice::po union member
data.value.po.number = ...; // populate the PO

While the gSOAP serializers are designed to be robust, failing to set the selector to a valid union member can lead to a crash of the serializer, because it will attempt to serialize an invalid union member.

The deserializer of a union type sets the selector value to the currently active union member that was deserialized. The selector will be set to a non-positive value (0 or -1) when no union member was deserialized, if permitted by the validator, where -1 indicates that a member was required by validation rules, if the validator was non-strict. Strict validation enabled with SOAP_XML_STRICT results in a validation fault in this case.

When more than one union is used in a struct or class, the __union selectors should use $ to identify them and named to avoid name clashes, for example:

struct ns__composite
{
char *name;
$ int sel_value; // = SOAP_UNION_ns__PO_or_Invoice_[po|invoice]
union ns__PO_or_Invoice value;
$ int sel_data; // = SOAP_UNION_ns__Email_or_Fax_[email|fax]
union ns__Email_or_Fax data;
};

For additional details, see the C and C++ XML Data Bindings documentation.

🔝 Back to table of contents

Pointer type serialization

Basically, the serialization of a pointer amounts to the serialization of the data pointed to. However, if more than one pointer points to a node in a data structure to serialize, the node is either duplicated in the serialized output meaning the data is serialized as a tree, or the node is output only once in the serialized output meaning that the data is serialized as a graph. The latter is referred to as multi-reference encoding in SOAP 1.1/1.2 RPC encoding style. This style ensures that data structures maintain their structural integrity when transmitted, as intended by the true meaning of serialization. The SOAP_XML_GRAPH runtime flag can be used with plain non-SOAP XML to achieve the same.

To achieve this, the gSOAP serializers for SOAP RPC encoding and the SOAP_XML_GRAPH flag check for multi-referenced data in the data structure to serialize, i.e. the data nodes that are co-referenced by other nodes, by adding id-ref/href attributes to the XML output that refer to the co-referenced data. The soapcpp2 tool generates serializers that perform this check automatically on C/C++ pointers and smart pointers, such as std::shared_ptr. Furthermore, the soapcpp2 tool generates serializers that prevent infinite serialization when a cyclic data structure is serialized as a tree, by breaking the cycles, when using the SOAP document/literal style or when SOAP_XML_TREE is enabled with the SOAP RPC encoding style.

For additional details on the use of C/C++ pointers and smart pointers, see the C and C++ XML Data Bindings documentation.

🔝 Back to table of contents

Multi-referenced data serialization

A node in the data structure that is pointed to by more than one pointer is serialized as multi-reference data when the SOAP RPC encoding style is used or when SOAP_XML_GRAPH is enabled. This means that co-referenced data is identified in XML with a unique id attribute. References in XML are made with href (SOAP 1.1 RPC encoding), SOAP-ENC:ref (SOAP 1.2 RPC encoding), or ref (SOAP_XML_GRAPH) attributes to refer to the co-referenced data. See Section Run-time flags on options to control the serialization of multi-reference data. To turn multi-ref off, use SOAP_XML_TREE to process plain tree-based XML. To completely eliminate multi-ref serialization use the WITH_NOIDREF compile-time flag with all source code (including gsoap/stdsoap2.c and gsoap/stdsoap2.cpp) to permanently disable id-href processing.

Consider for example the following a linked list data structure:

typedef char *xsd__string;
struct ns__list
{
xsd__string value;
struct ns__list *next;
};

Suppose a cyclic linked list is created. The first node contains the value "abc" and points to a node with value "def" which in turn points to the first node. This is encoded as:

1 <ns:list id="1"">
2  <value>abc</value>
3  <next>
4  <value>def</value>
5  <next href="#1"/>
6  </next>
7 </ns:list>

In case multi-referenced data is received that "does not fit in a pointer-based structure", the data is copied. For example, the following two structs are similar, except that the first uses pointer-based members while the other uses non-pointer-based members:

typedef long xsd__int;
struct ns__record
{
xsd__int *a;
xsd__int *b;
} P;
struct ns__record
{
xsd__int a;
xsd__int b;
} R;
int main()
{
P.a = &n;
P.b = &n;
... //
}

Since both a and b members of P point to the same integer, the serialization of P produces a multi-reference in SOAP 1.1 RPC encoding:

1 <ns:record>
2  <a href="#1"/>
3  <b href="#1"/>
4 </ns:record>
5 <id id="1">123</id>

The deserialization of the content in the R data structure that does not use pointers to integers results in a copy of each multi-reference integer. Note that the two structs resemble the same XML data type because the trailing underscore will be ignored in XML encoding and decoding.

🔝 Back to table of contents

NULL pointers and nil elements

A NULL pointer is not serialized, unless the pointer member of a struct or class is declared in the interface header file as nillable with nullptr or in the unlikely case the pointer itself is pointed to by another pointer (but see Section Run-time flags to control the serialization of NULLs), for example:

struct X
{
int *p;
int **q;
int *r nullptr 1;
}

The types section of a WSDL description contains information on the "nillability" of data, which is declared as nullptr members where the 1 indicates that the member is required (minOccurs and maxOccurs are 1 set with 1:1 or simply 1).

Suppose pointer q points to pointer p and suppose p and r are NULL. In that case the X struct is serialized with SOAP_XML_GRAPH as:

1 <X>
2  <p id="1" xsi:nil="true"/>
3  <q ref="1/>
4  <r id="1" xsi:nil="true"/>
5 </X>

The deserializer reconstructs the struct X from this form of XML, thereby preserving the integrity of the data structure serialized.

When the deserializer encounters an XML element that has a xsi:nil="true" attribute but the corresponding C/C++ data is not a pointer or reference, the deserializer will terminate with a SOAP_NULL fault when the SOAP_XML_STRICT flag is set.

🔝 Back to table of contents

Void pointer serialization

Void pointers (void*) cannot be serialized in XML because the type of data referred to is untyped. To enable the serialization of void pointers that are members of structs and classes, you can insert a int __type member right before the void pointer member. The int __type member contains run time information on the type of the data pointed to by void* member in a struct/class to enable the serialization of this data. The int __type member is set to a SOAP_TYPE_T value, where T is the name of a type. The soapcpp2 tool generates the SOAP_TYPE_T definitions in soapH.h and uses them internally to uniquely identify the type of each object. The type naming conventions outlined in Section Serializing C/C++ data to XML are used to determine the type name for T. Values serialized in XML with this approach always carry the xsi:type attribute in XML to indicate the type of content serialized.

Here is an example to illustrate the serialization of a void* member in a struct/class:

struct ns__record
{
int __type; // the SOAP_TYPE_T pointed to by val
void *val; // serialize any type in element <val>
};

The __type integer can be set to 0 at run time to omit the serialization of the void pointer member.

The following example illustrates the initialization of myStruct with a void pointer to an int:

struct ns__record S;
int n = 123;
S.val = (void*)&n;
S.__type = SOAP_TYPE_int;

The serialized output of S contains the integer in its val element:

1 <ns:record>
2  <val xsi:type="xsd:int">123</val>
3 </ns:record>

The deserializer for ns__record will automatically set the __type field and void pointer when deserializing the data, provided that the XML element val carries the xsi:type attribute from which it can determine the type.

Note
when serializing strings via a void* member, the void* pointer must directly point to the string value rather than indirectly as with all other types. For example:
struct ns__record S;
S.val = (void*)"Hello";
S.__type = SOAP_TYPE_string;

This is the case for all string-based types, including types defined with typedef char*.

You may use an arbitrary suffix with the __type members to handle multiple void pointers in structs/classes. For example:

struct ns__record
{
int __typeOfp; // the SOAP_TYPE_T pointed to by p
void *p; // element <p>
int __typeOfq; // the SOAP_TYPE_T pointed to by q
void *q; // element <q>
};

Because service method parameters are stored within structs, you can use __type and void* parameters to pass polymorphic arguments without having to define a C++ class hierarchy (Section Polymorphism, derived types, and dynamic binding in C++ ), provided that xsi:type attributes are present in the XML elements. For example:

typedef char *xsd__string;
typedef int xsd__int;
typedef float xsd__float;
enum ns__status { on, off };
struct ns__widget
{
char *name;
int part;
};
int ns__webmethod(int __type, void *data, struct ns__webmethodResponse { int __type; void *return_; } *out);

This method has a polymorphic input parameter data and a polymorphic output parameter return_. The __type parameters can be one of SOAP_TYPE_xsd__string, SOAP_TYPE_xsd__int, SOAP_TYPE_xsd__float, SOAP_TYPE_ns__status, or SOAP_TYPE_ns__widget. The WSDL and XSD files produced by the soapcpp2 tool declare the void* polymorphic members as xsd:anyType elements.

To declare a wrapper struct/class for void* pointers allows us to reuse this mechanism when we use __self as a member name that refers to the current XML element tag name:

struct __any
{
int __type; // the SOAP_TYPE_T pointed to by __self
void *__self; // serialize any type of content of the current element
};
struct ns__record
{
__any val;
};

The following example illustrates the initialization of __ns__record with a void pointer to an int:

struct ns__record S;
int n = 123;
S.val.__item = (void*)&n;
S.val.__type = SOAP_TYPE_int;

The serialized output of S contains the integer:

1 <ns:record>
2  <val xsi:type="xsd:int">123</val>
3 </ns:record>

🔝 Back to table of contents

Fixed-size array serialization

Fixed size arrays are serialized as repetitions of item elements with the array values in XML. Multi-dimensional fixed size arrays are serialized as nested item elements, where the outer elements are arrays.

The serialization of fixed-size arrays supports the SOAP RPC encoding multi-dimensional array format as well as partially transmitted and sparse array formats standardized in SOAP 1.1 and 1.2.

For example:

// Contents of file "fixed.h":
struct Example
{
float a[3];
};

This specifies a fixed-size array part of the struct Example. The serialized output of array a is:

1 <a>
2  <item>1.0</item>
3  <item>2.0</item>
4  <item>3.0</item>
5 </a>

Any deserialized items of an array that do not fit in the fixed size array, i.e. are out of bounds, are ignored by the deserializer when the SOAP_C_NOIOB flag is set, otherwise SOAP_IOB errors will be generated by the deserializer.

🔝 Back to table of contents

Dynamic array serialization

Dynamic arrays are much more flexible than fixed-size arrays. Dynamic arrays declared in the interface header file for soapcpp2 are a special struct or class or are part of a struct or class with a member pointing to an array of elements and a member that stores the size of the array. Dynamic array allocations are easy using the soapcpp-generated soap_new_T functions for type T. This function is used to allocate an array of values which can then be assigned to the pointer member of the struct/class that stores the array pointer with its size.

To facilitate SOAP RPC encoding, SOAP-encoded arrays require special treatment. SOAP-encoded arrays are single- or multi-dimensional arrays with bounds that appear in XML. These arrays may also have offsets that differ from zero. The intent of SOAP-encoded arrays is to replicate multi-dimensional arrays commonly found in programming languages.

However, XML also provides a simple way to represent a sequence of values with a sequence of XML elements. This differs from SOAP-encoded arrays in that SOAP-encoded arrays are elements with nested item elements with values, though SOAP deserializers may ignore the name of these elements when parsing XML as stated in the SOAP specifications.

Both SOAP-encoded arrays and sequences of XML elements are supported in gSOAP, using dynamic arrays and containers. The basics will be described next. For additional details, see the C and C++ XML Data Bindings documentation.

🔝 Back to table of contents

SOAP-encoded array bounds

SOAP-encoded arrays use the SOAP-ENC:Array attribute in XML to identify the array and the SOAP-ENC:arrayType attribute to identify the array dimensionality and its size.

As a security measure to avoid denial of service attacks based on sending a huge array size value using the SOAP-ENC:arrayType attribute, requiring the allocation of large chunks of memory, the total number of array elements set by the SOAP-ENC:arrayType attribute cannot exceed SOAP_MAXARRAYSIZE, which is set to 100000 by default. This limit is not a hard limit on the number of array elements, but rather to avoid pre-allocating large arrays as stated. The hard limit on the number of array elements received is soap::maxoccurs which is set to SOAP_MAXOCCURS by default. By contrast, the SOAP_MAXARRAYSIZE limit only negatively affects multi-dimensional arrays because the dimensionality of the receiving array may be lost when the number of elements exceeds 100000. One-dimensional arrays are not affected and populated after this limit by simply deserializing the array elements received.

🔝 Back to table of contents

One-dimensional dynamic SOAP-encoded arrays

A special form of struct or class is used to define one-dimensional dynamic SOAP-encoded arrays in an interface header file for soapcpp2. Each array has a pointer variable and a member that records the number of elements the pointer points to in memory.

The general form of the struct or class declaration that contains a one-dimensional dynamic SOAP-encoded array is:

struct array_name
{
Type *__ptr; // pointer to array of elements in memory
int __size; // number of elements pointed to
int __offset; // optional SOAP 1.1 array offset
... // anything that follows here will be ignored
};

where the array_name must be a non-qualified name and Type is the type for the elements of the array. The __ptr member points to the array values and __size is the array size. The __offset member specifies an optional array offset, when nonzero, see Section One-dimensional dynamic SOAP-encoded arrays with non-zero offsets.

If the array_name is qualified with a namespace prefix then the array is not a SOAP-encoded array but rather represents a sequence of XML elements, see Section Non-SOAP dynamic arrays.

The soapcpp2-generated deserializer of a one-dimensional dynamic array can deserialize partially transmitted and/or SOAP-encoded sparse arrays, and even multi-dimensional arrays which will be collapsed into a one-dimensional array with row-major ordering.

Warning
SOAP 1.2 does not support partially transmitted arrays and the __offset member of a dynamic array is ignored.

🔝 Back to table of contents

One-dimensional dynamic SOAP-encoded arrays with non-zero offsets

The declaration of a dynamic array as described in Section One-dimensional dynamic SOAP-encoded arrays may include an int __offset member. When set to an integer value, the serializer of the dynamic array will use this member as the start index of the array and the SOAP-encoded array offset attribute SOAP-ENC:offset will appear in the XML message. Note that array offsets is a SOAP 1.1 specific feature which is not supported in SOAP 1.2.

For example, the following header file declares a numeric Vector class, which is a dynamic array of floating point values with an index that starts at 1:

// Contents of file "vector.h":
class Vector
{ public:
float *__ptr;
int __size;
int __offset;
Vector();
Vector(struct soap *, int n);
float& operator[](int i);
struct soap *soap;
};

The implementations of the Vector methods are:

Vector::Vector()
{
this->soap_default(NULL);
}
Vector::Vector(struct soap *soap, int n)
{
this->soap = soap;
__ptr = soap_new_float(soap, n);
__size = n;
__offset = 1;
}
float& Vector::operator[](int i)
{
return __ptr[i - __offset];
}

An example program fragment that serializes a vector of 3 elements:

struct soap *soap = soap_new();
Vector v(soap, 3);
v[1] = 1.0;
v[2] = 2.0;
v[3] = 3.0;
soap_write_Vector(soap, &v);
soap_end(soap);
soap_free(soap);

The output is a partially transmitted array:

1 <SOAP-ENC:Array SOAP-ENC:arrayType="xsd:float[4]" SOAP-ENC:offset="[1]">
2  <item>1</item>
3  <item>2</item>
4  <item>3</item>
5 </SOAP-ENC:Array>

Note that xsd:float[4] is the type and shape of the encoded array, which starts at offset 1 and therefore the element at 0 is omitted.

🔝 Back to table of contents

Nested one-dimensional dynamic SOAP-encoded arrays

One-dimensional SOAP-encoded dynamic arrays may be nested. For example, using class Vector declared in the previous section, class Matrix is declared:

// Contents of file "matrix.h":
class Matrix
{ public:
Vector *__ptr;
int __size;
int __offset;
Matrix();
Matrix(struct soap *soap, int n, int m);
Vector& operator[](int i);
struct soap *soap;
};

The Matrix type is essentially an array of pointers to arrays which make up the rows of a matrix. The serialization of the two-dimensional dynamic array in is nested form in XML.

🔝 Back to table of contents

Multi-dimensional dynamic SOAP-encoded arrays

A special form of struct or class is used to define multi-dimensional dynamic SOAP-encoded arrays. Each array has a pointer variable and a member that records the number of elements per dimension. A K-dimensional array is declared as:

struct array_name
{
Type *__ptr; // pointer to array of elements in memory
int __size[K]; // number of elements per dimension
int __offset[K]; // optional SOAP 1.1 array offset
... // anything that follows here will be ignored
};

where the array_name must be a non-qualified name and Type is the type for the elements of the array. The __ptr member points to the array values. The __size array specifies the number of array elements per dimension. The __offset array specifies an optional offset per dimension.

For example, the following declaration specifies a matrix class:

class Matrix
{ public:
float *__ptr;
int __size[2];
int __offset[2];
};

By contrast to the matrix class of Section Nested one-dimensional dynamic SOAP-encoded arrays that defines a matrix as an array of pointers to matrix rows, this class has one pointer to a matrix stored in row-major order. The size of the matrix is determined by the __size member: __size[0] holds the number of rows and __size[1] holds the number of columns of the matrix. Likewise, __offset[0] is the row offset and __offset[1] is the columns offset.

Warning
SOAP 1.2 does not support partially transmitted arrays and the __offset member of a dynamic array is ignored.

🔝 Back to table of contents

Non-SOAP dynamic arrays

An array is serialized as a sequence of XML elements. By contrast, a SOAP-encoded array is serialized as an element with a sequence of sub-elements, whose tag names are irrelevant to the SOAP processor, see One-dimensional dynamic SOAP-encoded arrays.

An array is declared in an interface header file for soapcpp2 as a struct or class with a name that is qualified with a namespace prefix. There are two forms. The first form is similar to the SOAP-encoded array declaration that wraps the __ptr and __size members:

struct prefix__array_name
{
Type *__ptr; // pointer to array of elements in memory
int __size; // number of elements pointed to
... // anything that follows here will be ignored
};

The second form is more generic, because the array can be declared anywhere in the struct or class and multiple arrays can be used as members, each with a __size member (__sizeName is also allowed) that precedes a pointer member:

struct prefix__array_name
{
... // other members that are serialized
int __size_of_array1; // number of elements pointed to
Type1 *array1; // pointer to array of elements in memory
... // other members that are serialized
int __size_of_array1; // number of elements pointed to
Type2 *array2; // pointer to array of elements in memory
... // other members that are serialized
};

The __size member should be an int type and cannot be a size_t type or other integer type.

For example, we define a Map structure that contains a sequence of key-val pairs:

struct ns__Map
{
int __size; // number of pairs
struct ns__Pair
{
char *key;
char *val;
} *pair; // array of pairs
};

Since 2.7.16 it is also possible to use a '$' as a special marker to annotate a size member instead of requiring these members to start with __size:

struct ns__Map
{
$ int size; // number of pairs
struct ns__Pair
{
char *key;
char *val;
} *pair; // array of pairs
};

The array will be serialized in XML as a sequence of pairs:

1 <ns:Map>
2  <pair>
3  <key>Joe</key>
4  <val>555 77 1234</val>
5  </pair>
6  <pair>
7  <key>Susan</key>
8  <val>555 12 6725</val>
9  </pair>
10  <pair>
11  <key>Pete</key>
12  <val>555 99 4321</val>
13  </pair>
14 </ns:Map>

Deserialization is less efficient compared to a SOAP-encoded array, because the size of the sequence is not part of the SOAP encoding. Buffering is used by the deserializer to collect the elements in memory. When the end of the list is reached, the buffered elements are copied to a newly allocated managed space on the heap for the dynamic array.

Multiple arrays can be part of a struct or class. For example:

struct ns__Contact
{
char *firstName;
char *lastName;
$ int nPhones; // number of Phones
ULONG64 *phoneNumber; // array of phone numbers
$ int nEmails; // number of emails
char **emailAddress; // array of email addresses
};

The XML serialization of an example ns__Contact is:

1 <ns:Contact>
2  <firstName>Joe</firstName>
3  <lastName>Smith</lastName>
4  <phoneNumber>5551112222</phoneNumber>
5  <phoneNumber>5551234567</phoneNumber>
6  <phoneNumber>5552348901</phoneNumber>
7  <emailAddress>Joe.Smith@mail.com</emailAddress>
8  <emailAddress>Joe@Smith.com</emailAddress>
9 </ns:Contact>

For C++, a better alternative to arrays are containers, which are described next.

🔝 Back to table of contents

Containers

The containers std::deque, std::list, std::set, and std::vector are serializable in XML by the soapcpp2-generated serializers.

In order to use containers in an interface header file for soapcpp2, import stldeque.h, stllist.h, stlset.h, or stlvector.h to enable std::deque, std::list, std::set, and std::vector, respectively. For example:

#import "stlvector.h"
class ns__myClass
{ public:
std::vector<int> number 1:10; // 1 to 10 numbers
std::vector<std::string> *name 2; // more than 2 names
};

The use of pointer members such as for name shown above is possible, but not required. Also minOccurs : maxOccurs and minOccurs length constraints can be specified as shown in the example above. The XML schema that corresponds to the ns__myClass type is:

1 <complexType name="myClass">
2  <sequence>
3  <element name="number" type="xsd:int" minOccurs="1" maxOccurs="10"/>
4  <element name="name" type="xsd:string" minOccurs="2" maxOccurs="unbounded"/>
5  </sequence>
6 </complexType>

You can also implement your own containers. The containers must be class templates and should define a forward iterator type, and provide the following methods:

  • void clear() empty the container;
  • iterator begin() return iterator to beginning;
  • const_iterator begin() const return const iterator to beginning;
  • iterator end() return iterator to end;
  • const_iterator end() const return const iterator to end;
  • size_t size() return size;
  • bool empty() return true if empy;
  • iterator insert(iterator pos, const_reference val) insert element.

The iterator should be a forward iterator with a dereference operator to access the container's elements, it must be comparable (equal/unequal), and be pre-incrementable (++it). The const iterator is used by its soapcpp2-generated serializer to send a sequence of XML element values. The insert method is used to populate a container with Container::iterator i = container.insert(container.end(), val).

Here is in example container template class:

// simple_vector.h
template <class T>
class simple_vector
{ public:
typedef T value_type;
typedef value_type * pointer;
typedef const value_type * const_pointer;
typedef value_type & reference;
typedef const value_type & const_reference;
typedef pointer iterator;
typedef const_pointer const_iterator;
protected:
iterator head;
iterator tail;
size_t capacity;
public:
simple_vector() { head = tail = NULL; }
simple_vector(const simple_vector& v)
{ operator=(v); }
~simple_vector() { if (head) delete[] head; }
void clear() { tail = head; }
/* the member functions below are required for serialization of templates */
iterator begin() { return head; }
const_iterator begin() const { return head; }
iterator end() { return tail; }
const_iterator end() const { return tail; }
size_t size() const { return tail - head; }
bool empty() const { return head == tail; }
iterator insert(iterator pos, const_reference val)
{
if (!head)
head = tail = new value_type[capacity = 1];
else if (tail >= head + capacity)
{
iterator i = head;
iterator j = new value_type[capacity *= 2];
iterator k = j;
while (i < tail)
*k++ = *i++;
if (pos)
pos = j + (pos - head);
tail = j + (tail - head);
delete[] head;
head = j;
}
if (pos && pos >= head && pos < tail)
{
iterator i = tail;
iterator j = i - 1;
while (j != pos)
*i-- = *j--;
*pos = val;
}
else
{
pos = tail;
*tail++ = val;
}
return pos;
}
simple_vector& operator=(const simple_vector& v)
{
head = tail = NULL;
capacity = v.capacity;
if (v.head)
{
head = tail = new value_type[capacity];
iterator i = v.head;
while (i != v.tail)
*tail++ = *i++;
}
return *this;
}
};

To enable the container, we add the following two lines to the interface header file for soapcpp2:

#include "simpleVector.h"
template <class T> class simpleVector;

The container class itself should not be defined in the interface header file, only the template declaration suffices for soapcpp2 to generate serializers. Recall that the #include directives are not executed by soapcpp2 but simply passed on to the generated source code. This include specifies in the generated source code where the container is actually defined.

🔝 Back to table of contents

Polymorphic dynamic arrays and lists

Polymorphic arrays, that is, arrays of values of any type, can be serialized in XML when declared as an array of pointers to a base class. For example:

class ns__Object
{ public:
... // members of ns__Object
};
class ns__Data : public ns__Object
{ public:
... // members of ns__Data
};
class ArrayOfObject
{ public:
ns__Object **__ptr; // pointer to array of pointers to base or derived objects
int __size; // size of the array
};
class ns__Objects
{ public:
std::vector<ns__Object> objects; // vector of base or derived objects
};

The pointers in the array can point to the ns__Object base instances or ns__Data derived instances, which will be serialized accordingly in XML. Derived instances are indicated by xsi:type attribute in XML with the qualified name of the class, to distinguish derived instances from the base instances. Without this attribute the deserializer will not instantiate the derived instance but a base instance since there is no identifying information to distinguish the XML forms except for the xsi:type attribute.

Since we cannot use dynamic binding to support polymorphism in C, another mechanism we can use is void pointers . Here is an example of a polymorphic SOAP-encoded array ArrayOfObject and a non-SOAP dynamic array ns__Objects that hold values of any serializable type:

struct __wrapper
{
int __type; // identify the type below by SOAP_TYPE_T
void *__item; // pointer to data of type T
};
struct ArrayOfObject
{
struct __wrapper *__ptr; // pointer to array
int __size; // size of the array
};
struct ns__Objects
{
int __size; // size of the array
struct __wrapper *objects; // pointer to array
};

This example uses an "invisible" type __wrapper and member __array, which start with a double underscore. These names are never visible in serialized XML. The __type member of __wrapper is a SOAP_TYPE_T value that identifies the type T that __item points to, see Section Void pointer serialization.

🔝 Back to table of contents

How to change the tag names of array item elements

The default XML element tag name for array elements is item, which can be changed. The __ptr member in a struct or class of a dynamic array may have an optional suffix part that specifies the name of the element tag in XML. That is, the suffix is part of the __ptr member name:

Type *__ptrarray_elt_name

Consider for example:

struct ArrayOfstring
{
char* *__ptrstring;
int __size;
};

The array is serialized as:

1 <SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[2]">
2  <string>Hello</string>
3  <string>World</string>
4 </SOAP-ENC:Array>

SOAP 1.1/1.2 does not mandate a specific tag name for SOAP-encoded array elements and the soapcpp2-generated serializers will ignore the name used to itemize SOAP-encoded array values.

🔝 Back to table of contents

base64Binary serialization

The base64Binary XSD type is introduced in an interface header file for soapcpp2 using a struct or class that contains an array of unsigned char values:

struct xsd__base64Binary
{
unsigned char *__ptr;
int __size;
};

The advantage of this struct or class is the ability to serializer raw binary data from memory, since the soapcpp2-generated serializer converts the binary data to/from base64 in XML.

To introduce a new XML schema type derived from base64Binary use the same struct or class structure, but with another name. For example:

struct ns__binary
{
unsigned char *__ptr;
int __size;
};

The resulting XML schema type is:

1 <simpleType name="binary">
2  <restriction base="xsd:base64Binary">
3  </restriction>
4 </simpleType>

🔝 Back to table of contents

hexBinary serialization

The base64Binary XSD type is introduced in an interface header file for soapcpp2 using a struct or class that contains an array of unsigned char values:

struct xsd__hexBinary
{
unsigned char *__ptr;
int __size;
};

The advantage of this struct or class is the ability to serializer raw binary data from memory, since the soapcpp2-generated serializer converts the binary data to/from hexadecimal in XML.

If a binary type such as xsd__base64Binary is already defined, then we can simply use a typedef to introduce the hex variant:

class xsd__base64Binary
{ public:
unsigned char *__ptr;
int __size;
};
typedef xsd__base64Binary xsd__hexBinary; // serializes into hex content

This lets soapcpp2 generate xsd:base64Binary and xsd:hexBinary serializers.

🔝 Back to table of contents

SOAP RPC encoded versus document/literal style

SOAP has several styles:

  • SOAP RPC encoding uses XML that is restricted to SOAP structures to ensure programming-language interoperability. Not allowed are values serialized as XML attributes, arrays should be serialized as SOAP-encoded arrays instead of XML element repetitions (i.e. xsd:sequence maxOccurs="unbounded" is not allowed), and xsd:choice components are not allowed. Multi-referenced elements are used to serialize data structure graphs. Because additional SOAP-encoding specific attributes are present that are not defined in the XML schema (of the WSDL), strict XML schema validators may reject SOAP-encoded content. The SOAP Body contains at most one service operation request element or at most one service operation response element and the encoding style is indicated with the SOAP-ENV:encodingStyle="..." attribute in the SOAP Body or one or more of its sub-elements. This style is specified for the entire service declared under namespace prefix ns with:
    //gsoap ns service style: rpc
    //gsoap ns service encoding: encoded
  • SOAP document/literal uses XML constrained to the XML schema that defines the XML content. The serialization of tree-based data structures is accurate in XML. The serialization of digraph-shaped data structures results in the duplication of data nodes that are co-references. Cyclic data structures cannot be accurately serialized, but you can use SOAP_XML_GRAPH to force the use of id-ref to accurately serialize digraphs and cyclic data structures. The SOAP Body may contain any number of XML elements, as if the SOAP Body is the root of an XML document. No SOAP-ENV:encodingStyle="..." attribute should appear in literal content. This style is specified for the entire service declared under namespace prefix ns with:
    //gsoap ns service style: document
    //gsoap ns service encoding: literal
  • SOAP RPC literal also uses XML constrained to the XML schema that defines the XML content. The difference with document/literal is that the SOAP Body contains at most one service operation request element or at most one service operation response element. No SOAP-ENV:encodingStyle="..." attribute should appear in literal content. This style is specified for the entire service declared under namespace prefix ns with:
    //gsoap ns service style: rpc
    //gsoap ns service encoding: literal

Besides //gsoap ns service style and //gsoap ns service encoding there are also the service operation specific versions //gsoap ns service method-style, //gsoap ns service method-response-style, //gsoap ns service method-encoding, and //gsoap ns service method-response-encoding that explicitly specify SOAP RPC encoded, document/literal, or RPC literal style messages for the indicated service operation methods.

To enable SOAP RPC encoding for a particular service operation, use:

//gsoap ns service method-style: webmethod rpc
//gsoap ns service method-encoding: webmethod encoded
int ns__webmethod(...)

To enable SOAP RPC encoding for a particular service operation response, use:

//gsoap ns service method-response-style: webmethod rpc
//gsoap ns service method-response-encoding: webmethod encoded
int ns__webmethod(...)

Likewise, you can specify document/literal and RPC literal messages. The default style is document/literal, unless soapcpp2 -e option -e is used to set SOAP RPC encoding by default.

For the style directives you can specify rpc or document. For the encoding directives you can specify literal, encoded, or even a custom URI that indicates some custom or proprietary encoding format in XML which will not interoperate with SOAP processors that are not compatible with the specified encoding format. See also Section Directives.

See also C and C++ XML data bindings documentation for differences in XML serialization when using the SOAP RPC encoded and document/literal messaging styles.

🔝 Back to table of contents

Serializing mixed content with literal XML strings

XML is stored in "literal" XML strings which are the built-in _XML type that is a regular char* string or you can declare a wide character string in an interface header file for soapcpp2 as follows:

typedef wchar_t *XML;

To declare a C++ std::string literal XML type:

typedef std::string XML;

Or use a wide character string:

typedef std::wstring XML;

To use both at the same time:

typedef std::string XML;
typedef std::wstring XML_;

The differences between the use of regular 8-bit strings versus wide character strings for XML documents are:

  • XML literal strings must hold UTF-8 XML content.
  • Wide character XML literal strings are converted to and from UTF-8.

🔝 Back to table of contents

XML validation

Some XML validation constraints are not automatically verified unless explicitly set using the SOAP_XML_STRICT flag. SOAP RPC encoding is an XML format that does not afford strict XML validation, because of the addition of SOAP-specific attributes and other small deviations that will be detected by an aggressive XML validator, leading the messaging failures. By toning XML validation down, it helps to improve SOAP RPC encoding interoperability.

Strict validation constraints are enabled with the SOAP_XML_STRICT mode flag set, e.g. with soap_set_imode(soap, SOAP_XML_STRICT) or soap_new(SOAP_XML_STRICT), see Section Run-time flags for the complete list of flags.

The next sections describe the type of constraints validated when SOAP_XML_STRICT is enabled and validation constraints are specified in the interface header file.

Use compiler flag WITH_REPLACE_ILLEGAL_UTF8 to force strict UTF-8 text conversions, which replaces invalid UTF-8 with U+FFFD.

See also C and C++ XML data bindings documentation for more details.

🔝 Back to table of contents

Default values

Default values can be defined for optional elements and attributes, which means that the default value will be used when the element or attribute value is not present in the parsed XML. See also Section Default values for omitted XML elements and attributes and examples in subsequent subsections below.

Default values must be primitive types, integer, float, string, etc. or pointers to primitive types. Default values can be specified for struct and class members, as shown in the example below:

struct ns__MyRecord
{
int n = 5; // optional element with default value 5
char *name = "none"; // optional element with default value "none"
@ enum ns__color { RED, WHITE, BLUE } color = RED; // optional attribute with default value RED
};

Upon deserialization of absent data, these members will be set accordingly. When classes are instantiated with soap_new_ClassName the instance will be initialized with default values.

See also C and C++ XML data bindings documentation for more details.

🔝 Back to table of contents

Occurrence constraints

Occurrence constraints specify the minimum and/or maximum frequency or optionality of of attributes and elements.

🔝 Back to table of contents

Elements with minOccurs and maxOccurs restrictions

To force the validation of minOccurs and maxOccurs constraints the SOAP_XML_STRICT input mode flag must be set. The minOccurs and maxOccurs constraints are specified for members of a struct and members of a class in a header file using the following syntax:

Type membername nullptr minOccurs : maxOccurs = value;

The nullptr is optional and indicates that the member is nillable (gSOAP version 2.8.24 or greater), which means that when NULL an empty element with xsi:nil="true" is added in the serialized XML.

The minOccurs and maxOccurs are optional values that must be integer literals. When maxOccurs is not specified then the colon can be omitted. When minOccurs is not specified it is assumed to be one (1) for non-pointer members that are elements and zero (0) for members that are pointers or are attributes (i.e. have a @ qualifier).

A default initialization value may be provided and is optional.

A fixed initialization value can be specified with == (gSOAP version 2.8.48 or greater).

For example

struct ns__MyRecord
{
int n 0 = 5; // element with default value 5, minOccurs=0, maxOccurs=1
int m; // element with minOccurs=1
int *k nullptr 1; // element with minOccurs=1 and nillable=true
int v == 2; // element with minOccurs=1 and fixed value 2
int __size 0:10; // sequence <item> with minOccurs=0, maxOccurs=10
int *item;
std::vector<double> nums 2; // sequence <nums> with minOccurs=2, maxOccurs=unbounded
};
struct arrayOfint
{
int *__ptr 1:100; // minOccurs=1, maxOccurs=100
int size;
};

See also C and C++ XML data bindings documentation for more details.

🔝 Back to table of contents

Required and prohibited attributes

Similar to the minOccurs and maxOccurs annotations defined in the previous section, attributes in a struct or class can be annotated with occurrence constraints to make them optional (0), required (1), or prohibited (0:0). Default values can be assigned to optional attributes.

For example

struct ns__MyRecord
{
@ int m 1; // required attribute (occurs at least once)
@ int n = 5; // optional attribute with default value 5
@ int o 0; // optional attribute (may or may not occur)
@ int p 0:0; // prohibited attribute
};

Remember to set the SOAP_XML_STRICT input mode flag to enable the validation of attribute occurrence constraints.

See also C and C++ XML data bindings documentation for more details.

🔝 Back to table of contents

Value constraints

Value constraints restrict the length of strings and the range of values of numeric types.

🔝 Back to table of contents

Data length restrictions

A schema simpleType is defined with a typedef by taking a base primitive to defined a derived simpleType. For example:

typedef int time__seconds;

This defines the following schema type in time.xsd:

1 <simpleType name="seconds">
2  <restriction base="xsd:int"/>
3 </simpleType>

A complexType with simpleContent is defined with a wrapper struct/class:

struct time__date
{
char *__item; // some custom format date (restriction of string)
@ enum time__zone { EST, GMT, ... } zone;
}

This defines the following schema type in time.xsd:

1 <complexType name="date">
2  <simpleContent>
3  <extension base="xsd:string"/>
4  </simpleContent>
5  <attribute name="zone" type="time:zone" use="optional"/>
6 </complexType>
7 <simpleType name="zone">
8  <restriction base="xsd:string">
9  <enumeration value="EST"/>
10  <enumeration value="GMT"/>
11  ...
12  </restriction>
13 </simpleType>

Data value length constraints of simpleTypes and complexTypes with simpleContent are defined as follows:

typedef char *ns__string256 0:256; // simpleType restriction of string with max length 256 characters
typedef char *ns__string10 10:10; // simpleType restriction of string with length of 10 characters
typedef std::string *ns__string8 8; // simpleType restriction of string with at least 8 characters
struct ns__data // simpleContent wrapper
{
char *__item :256; // simpleContent with at most 256 characters
@ char *name 1; // required name attribute
};
struct time__date // simpleContent wrapper
{
char *__item :100;
@ enum time__zone { EST, GMT, ... } zone = GMT;
}

Set the SOAP_XML_STRICT mode flag to enable the validation of value length constraints.

See also C and C++ XML data bindings documentation for more details.

🔝 Back to table of contents

Value range restrictions

Similar to data length constraints for string-based data, integer and floating point value range constraints on numeric simpleTypes and complexTypes with simpleContent are declared with low : high, where low and high are optional.

As of gSOAP 2.8.26, floating point value ranges and integer ranges can be exclusive by adding < on either side of the : range operator:

range validation check
1 1 <= x
1 : 1 <= x
: 10 x <= 10
1 : 10 1 <= x <= 10
1 < : < 10 1 < x < 10
1 < 10 1 < x < 10
1 : < 10 1 <= x < 10
: < 10 x < 10
< 10 x < 10
1 < : 1 < x
1 < 1 < x
1 < : 10 1 < x <= 10

For example:

typedef int ns__int10 0:10; // simpleType restriction of int 0..10
typedef LONG64 ns__long -1000000:1000000; // simpleType restriction of long64 -1000000..1000000
typedef float ns__float -1.0 <:< 10.5; // simpleType restriction of float in (-1,10.5)
struct ns__data // simpleContent wrapper
{
int __item 0:10; // simpleContent range 0..10
@ char *name 1; // required name attribute
};

Set the SOAP_XML_STRICT mode flag to enable the validation of value range constraints.

See also C and C++ XML data bindings documentation for more details.

🔝 Back to table of contents

Pattern restrictions

Patterns can be defined for simpleType content. However, pattern validation is not enforced unless the soap::fsvalidate and soap::fwvalidate callbacks are set to a regex matcher.

To associate a pattern with a simpleType, you can define a simpleType with a typedef and a pattern string:

typedef int time__second "[1-5]?[0-9]|60";

This defines the following schema type in time.xsd:

1 <simpleType name="second">
2  <restriction base="xsd:int">
3  <pattern value="[1-5]?[0-9]|60"/>
4  </restriction>
5 </simpleType>

The pattern string must contain a valid regular expression.

A special case for C format string patterns is introduced in gSOAP 2.8.18. When xsd:totalDigits and xsd:fractionDigits are given in a XSD file, then a C format string is produced to output floating point values with the proper precision and scale. For example:

1 <simpleType name="ratio">
2  <restriction base="xsd:float">
3  <totalDigits value="5"/>
4  <fractionDigits value="2"/>
5  </restriction>
6 </simpleType>

produces:

typedef float time__ratio "%5.2f";

The format string is used to format the output the floating point value in XML.

See also C and C++ XML data bindings documentation for more details.

🔝 Back to table of contents

Element and attribute qualified/unqualified forms

Struct, class, and union members represent elements and attributes that are automatically qualified or unqualified depending on the schema element and attribute default forms specified. The engine always validates the prefixes of elements and attributes. When a namespace mismatch occurs, the element or attribute is not consumed which can lead to a validation error (unless the complexType is extensible or when SOAP_XML_STRICT is turned off).

Consider for example:

//gsoap ns schema elementForm: qualified
//gsoap ns schema attributeForm: unqualified
struct ns__record
{
@ char * type;
char * name;
};

Here, the ns__record struct is serialized with qualified element name and unqualified attribute type:

1 <ns:record type="...">
2  <ns:name>...</ns:name>
3 </ns:record>

The "colon notation" for struct/class/union member names is used to override element and attribute qualified or unqualified forms. To override the form for individual members that represent elements and attributes, use a namespace prefix and colon with the member name:

//gsoap ns schema elementForm: qualified
//gsoap ns schema attributeForm: unqualified
struct ns__record
{
@ char * ns:type;
char * :name;
};

where name is unqualified and type is qualified:

1 <ns:record ns:type="...">
2  <name>...</name>
3 </ns:record>

The colon notation is a syntactic notation used only in the interface header file syntax, it is not translated to the C/C++ output.

The colon notation does not avoid name clashes between members. For example:

struct x__record
{
@ char * name;
char * x:name;
};

results in a redefinition error, since both members have the same name. To avoid name clashes, use a underscore suffix:

struct x__record
{
@ char * name;
char * x:name_;
};

Not that the namespace prefix convention can be used instead:

struct x__record
{
@ char * name;
char * x__name;
};

which avoids the name clash. However, the resulting schema is different since the last example generates a global name element definition that is referenced by the local element.

More specifically, the difference between the namespace prefix convention with double underscores and colon notation is that the namespace prefix convention generates schema element/attribute references to elements/attributes at the top level, while the colon notation only affects the local element/attribute namespace qualification by form overriding. This is best illustrated by an example:

struct x__record
{
char * :name;
char * x:phone;
char * x__fax;
char * y__zip;
};

which generates the following x.xsdschema:

1 <complexType name="record">
2  <sequence>
3  <element name="name" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true" form="unqualified"/>
4  <element name="phone" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true" form="qualified"/>
5  <element ref="x:fax" minOccurs="0" maxOccurs="1"/>
6  <element ref="y:zip" minOccurs="0" maxOccurs="1"/>
7  </sequence>
8 </complexType>
9 <element name="fax" type="xsd:string"/>

and the y.xsd schema defines contains:

1 <element name="zip" type="xsd:string"/>

See also C and C++ XML data bindings documentation for more details.

🔝 Back to table of contents

XML namespaces and the namespace mapping table

A namespace mapping table should be included in the source code of client and service applications. The mapping table is used by the serializers and deserializers of the stub and skeleton functions to produce valid XML messages and to parse and validate XML messages. A typical mapping table is shown below:

struct Namespace namespaces[] =
{
// { "prefix", "URI", "URI-pattern" (optional) }
{ "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" }, // must be fourth
{ "ns", "urn:my-service-URI" }, // binds "ns" namespace prefix to schema URI
{ NULL, NULL } // end of table
};

Each namespace prefix used by a identifier name in the header file specification, see Section C/C++ identifier name to XML tag name translation, must have a binding to a namespace URI in the mapping table. The end of the namespace mapping table must be indicated by the NULL pair. The namespace URI matching is case insensitive. A namespace prefix is distinguished by the occurrence of a pair of underscores (__) in an identifier or by using colon notation, see Section C/C++ identifier name to XML tag name translation.

An optional third column in the namespace mapping table may be specified that contains a namespace URI pattern. The patterns provide an alternative namespace for the validation of parsed XML messages. In this pattern, dashes (-) are single-character wildcards and asterisks (*) are multi-character wildcards. For example, to accept alternative versions of XML schemas with different authoring dates, four dashes can be used in place of the specific dates in the namespace mapping table pattern:

struct Namespace namespaces[] =
{
// { "prefix", "URI", "URI-pattern" (optional) }
{ "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", "http://www.w3.org/----/XMLSchema-instance" },
{ "xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/----/XMLSchema" },
... //
{ NULL, NULL } // end of table
};

Or alternatively, asterisks can be used as wildcards for multiple characters:

struct Namespace namespaces[] =
{
// { "prefix", "URI", "URI-pattern" (optional) }
{ "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", "http://www.w3.org/*/XMLSchema-instance" },
{ "xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema" },
... //
{ NULL, NULL} // end of table
};

A namespace mapping table is automatically generated with prefixes and URIs in the table that are declared with //gsoap <prefix> schema namespace: directives in the interface header file, see Section Directives. If directives are not provided in the header file then default URIs of the form http://tempuri.org/prefix.xsd for each namespace prefix. The soapcpp2 tool also generates a WSDL and one or more XSD files, one for each XML namespace.

When parsing XML and deserializing data, namespace URIs in the XML messages are matched against the second and third column of the namespace mapping table, searching from the top to the bottom of the table. The actual prefix used in the XML message is irrelevant as the URI associated with the prefix is relevant to define the XML namespace to which a qualified element or attribute belongs. When a match is found, the namespace prefix in the first column of the table is considered semantically identical to the namespace prefix used by the qualified XML element and attribute parsed, even when the prefix names differ. This normalization of prefixes is invisible to the user of gSOAP and makes coding with XML easier. For example, when XSD QNames are parsed into strings using the built-in soapcpp2 _QName type or a QName declared with typedef std::string xsd__QName, then this QName string will always contain qualified names with normalized prefixes, i.e. prefixes defined in the namespace mapping table, unless the table has no entry, see Section How to use QName attributes and elements.

For example, let's say we have the following structs:

struct a__elt { ... };
struct b__elt { ... };
struct k__elt { ... };

The namespace mapping table generated by soapcpp2 has the following entries:

struct Namespace namespaces[] =
{
// { "prefix", "URI", "URI-pattern" (optional) }
... // the four SOAP and XSD namespace bindings
{ "a", "http://tempuri.org/a.xsd" },
{ "b", "http://tempuri.org/b.xsd" },
{ "c", "http://tempuri.org/c.xsd" },
... //
{ NULL, NULL }
};

Then, the following XML elements will match these structs:

1 <x:elt xmlns:x="http://tempuri.org/a.xsd">...</x:elt>
2 <elt xmlns="http://tempuri.org/b.xsd">...</elt>
3 <zzz:elt xmlns:zzz=http://tempuri.org/c.xsd">...</zzz:elt>

Instead of one big namespace table that contains all XML namespace prefixes with their URIs, there are cases when it is desirable to use multiple namespace tables, one for each service. This avoids leaking namespace prefixes in XML messages that have nothing to do with the service invoked. In principle there is no harm to leak these namespace prefixes, but the messages are less compact and not as clean. To use multiple namespace tables at the client side is done as follows:

struct Namespace namespacesTable1[] = { ... };
struct Namespace namespacesTable2[] = { ... };
struct Namespace namespacesTable3[] = { ... };
... //
struct soap *soap = soap_new();
soap_set_namespaces(soap, namespaceTable1); // use the first namespace table
if (soap_call_remote_method(soap, endpoint, Action, ...))
... // error

Likewise, on the server side call soap_set_namespaces before calling soap_serve. Changing the namespaces table in service operations has no effect.

The XML messages produced by the gSOAP engine include all xmlsn namespace bindings in the root element, which is generally more efficient for larger XML documents in which otherwise the xmlsn namespace bindings will be sprinkled throughout. By contrast, canonical XML requires xmlsn namespace bindings only to be included when utilized. Therefore, the SOAP_XML_CANONICAL context flag produces C14N exclusive XML messages and documents, which eliminates unused xmlsn namespace bindings from XML. Unfortunately, the current C14N standard is buggy with respect to XSD QName content, because prefixes used in QName content are not considered utilized. The gSOAP engine considers QName content prefixes utilized and therefore produces corrected canonicalized XML output that prevents the loss of namespace information for QNames.

🔝 Back to table of contents

SOAP Header processing

A built-in SOAP Header data structure SOAP_ENV__Header is generated by the soapcpp2 tool for exchanging SOAP headers in SOAP messages. This structure is empty unless headers are added by plugins and headers specified by WSDL specifications (i.e. wsdl2h adds SOAP Headers).

You can create your own SOAP Header struct simply by declaring it in an interface header file for soapcpp2 and by adding members that must be qualified with namespace prefixes to conform to the SOAP Header processing requirements that SOAP Header elements must be namespace qualified.

For example, assume that transaction data should be piggy-backed with SOAP messages in SOAP Header:

struct t__transaction
{
int number;
const char *dscription;
};
{
mustUnderstand struct t__transaction *t__transaction;
};
//gsoap ns service method-input-header-part: webmethod t__transaction
int ns__webmethod(...);

The mustUnderstand qualifier specifies that the element must be processed by the SOAP processor and cannot be ignored if the processor has no logic in place for this SOAP header, which is done by adding a SOAP-ENV:mustUnderstand="true" attribute to the t:transaction element. The soapcpp2-generated serializers obey this safety principle.

This declares a service operation that sends messages with an input SOAP header t__transaction, as can be seen in the generated WSDL binding:

1 <binding name="Service" type="tns:ServicePortType">
2  <SOAP:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
3  <operation name="webmethod">
4  <SOAP:operation soapAction=""/>
5  <input>
6  <SOAP:body use="literal" parts="Body"/>
7  <SOAP:header use="literal" message="tns:ServiceHeader" part="transaction"/>
8  </input>
9  <output>
10  <SOAP:body use="literal" parts="Body"/>
11  </output>
12  </operation>
13 </binding>

Likewise, you can specify that both input and output messages have the same header with //gsoap ns service method-header-part: or the output message has a header with //gsoap ns service method-output-header-part:. Multiple headers can be specified this way.

To set up a SOAP Header at the client side that contains the t__transaction member:

struct soap *soap = soap_new();
... //
soap.header = NULL; // erase any SOAP Header we have so far
soap_header(soap); // allocate and initialize a new SOAP Header
soap->header->t__transaction = soap_new_t__transaction(soap, -1);
soap->header->t__transaction->number = num;
soap->header->t__transaction->description = "Transactional data";
if (soap_call_webmethod(soap, endpoint, NULL, ...))
... // error

The SOAP Web service request includes the SOAP Header with the transaction data:

1 <SOAP-ENV:Envelope ...>
2  <SOAP-ENV:Header>
3  <t:transaction SOAP-ENV:mustUnderstand="true">
4  <number>12345</number>
5  <description>Transactional data</description>
6  </t:transaction>
7  </SOAP-ENV:Header>
8  <SOAP-ENV:Body>
9  <ns:webmethod>
10  ...
11  </ns:webmethod>
12  </SOAP-ENV:Body>
13 </SOAP-ENV:Envelope>

At the receiving side, a SOAP Header can be inspected by checking for a non-NULL soap::header.

Warning
When SOAP Headers are used, you must make sure to set soap::header to NULL when no SOAP Header should be sent, otherwise any SOAP Headers currently present in the SOAP_ENV__Header struct pointed to by soap::header will be included in the message.

At the client side, the soap::actor string variable can be set to set the SOAP SOAP-ENV:actor attribute. The SOAP-ENV:mustUnderstand="true" attribute then indicates the requirement that the recipient corresponding to the SOAP-ENV:actor attribute value is responsible to process the SOAP Header element. The details of which can be found in the SOAP 1.1/1.2 specifications that the gSOAP tools conform to.

The SOAP Header structure SOAP_ENV__Header is declared mutable, which means that re-declarations of the structures are permitted and additional members are collected into one final structure by the soapcpp2 tool.

For another example, consider:

{
char *h__transaction;
struct UserAuth *h__authentication;
};

Suppose method ns__login uses both header parts (at most), then this is declared as:

//gsoap ns service method-header-part: login h__transaction
//gsoap ns service method-header-part: login h__authentication
int ns__login(...);

Suppose method ns__search uses only the first header part, then this is declared as:

//gsoap ns service method-header-part: search h__transaction
int ns__search(...);

Note that the method name and header part names must be namespace qualified. The headers must be present in all operations that declared the header parts.

To specify the header parts for the method input (method request message), use:

//gsoap namespace-prefix service method-input-header-part: method-name header-part

Similarly, to specify the header parts for the method output (method response message), use:

//gsoap namespace-prefix service method-output-header-part: method-name header-part

The declarations only affect the WSDL. For example:

{
char *h__transaction;
struct UserAuth *h__authentication;
};
//gsoap ns service method-input-header-part: login h__authentication
//gsoap ns service method-input-header-part: login h__transaction
//gsoap ns service method-output-header-part: login h__transaction
int ns__login(...);

The headers must be present in all operations that declared the header parts.

See also API documentation Module Header structure and functions.

🔝 Back to table of contents

SOAP Fault processing

A built-in SOAP Fault data structure SOAP_ENV__Fault is generated by the soapcpp2 tool for exchanging exception messages. This structure has the general form:

{
_QName faultcode; // _QName is built-in
char *faultstring;
char *faultactor;
struct SOAP_ENV__Code *SOAP_ENV__Code; // must be a SOAP_ENV__Code struct defined below
struct SOAP_ENV__Detail *SOAP_ENV__Detail; // SOAP 1.2 detail member
};
{
_QName SOAP_ENV__Value;
};
{
int __type; // The SOAP_TYPE_ of the object serialized as Fault detail
void *fault; // pointer to the fault object, or NULL
_XML __any; // any other detail element content (stored in XML format)
};

The first four members in SOAP_ENV__Fault are SOAP 1.1 specific. The last five members are SOAP 1.2 specific. You can redefine these structures in the interface header file for soapcpp2. For example, you can use a class for the SOAP_ENV__Fault and add methods for convenience.

The SOAP_ENV__Detail structure can be changed to the needs of Web service application to communicate specific fault data structures, but this is generally not necessary because the application-specific SOAP Fault details can be serialized via the __type and fault members in the SOAP_ENV__Detail member, see Section Void pointer serialization on the serialization of data referred to by __type and fault.

When a user-define service operation function returns an error with soap_sender_fault or soap_receiver_fault, then the SOAP Fault structure is populated and soap::fault points to this SOAP Fault. The SOAP Fault is sent to the client. The client populates a SOAP Fault structure that contains the SOAP Fault message with details.

Server-side faults are raised with soap_sender_fault or soap_receiver_fault. The soap_sender_fault call should be used to inform that the sender is at fault and the sender (client) should not re-send the request. The soap_receiver_fault call should be used to indicate a temporary server-side problem, so a sender (client) can re-send the request later. For example:

int ns1__myMethod(struct soap *soap, ...)
{
... //
return soap_receiver_fault(soap, "Resource temporarily unavailable", NULL); // return fault to sender
}

In the example, the SOAP Fault details were empty (NULL). You may pass an XML fragment, which will be literally included in the SOAP Fault message. For WS-I Basic Profile compliance, you must pass an XML string with one or more namespace qualified elements, such as:

return soap_receiver_fault(soap, "Resource temporarily unavailable", "<errorcode xmlns='http://tempuri.org'>123</errorcode><errorinfo xmlns='http://tempuri.org'>abc</errorinfo>");

When a service operation needs to populate SOAP Fault details with a application-specific data, it does so by assigning the soap::fault member of the current reference to the context with appropriate data associated with the exception and by returning the error SOAP_FAULT. For example:

soap_receiver_fault(soap, "Error message", NULL);
if (soap->version == 2) // SOAP 1.2 is used
{
soap->fault->SOAP_ENV__Detail = soap_new_SOAP_ENV__Detail(soap, -1);
soap->fault->SOAP_ENV__Detail->__type = SOAP_TYPE_ns1__myStackDataType; // stack type
soap->fault->SOAP_ENV__Detail->fault = sp; // point to stack
soap->fault->SOAP_ENV__Detail->__any = NULL; // no other XML data
}
else
{
soap->fault->detail = soap_new_SOAP_ENV__Detail(soap, -1);
soap->fault->detail->__type = SOAP_TYPE_ns1__myStackDataType; // stack type
soap->fault->detail->fault = sp; // point to stack
soap->fault->detail->__any = NULL; // no other XML data
}
return SOAP_FAULT; // return from service operation call with the fault

Here, soap_receiver_fault allocates a fault struct then we set the SOAP Fault details as shown.

Note that SOAP 1.2 supports nested fault sub-codes. These can be set as follows:

struct SOAP_ENV__Code *subcode1 = soap_new_SOAP_ENV__Code(soap);
struct SOAP_ENV__Code *subcode2 = soap_new_SOAP_ENV__Code(soap);
soap_sender_fault(soap, "The requested profile token ProfileToken does not exist.", NULL);
subcode1->SOAP_ENV__Value = (char*)"ter:InvalidArgs"; // a QName value
subcode1->SOAP_ENV__Subcode = subcode2;
subcode2->SOAP_ENV__Value = (char*)"ter:NoProfile"; // a QName value
subcode2->SOAP_ENV__Subcode = NULL;
return SOAP_FAULT;

This produces:

1 <SOAP-ENV:Fault>
2  <SOAP-ENV:Code>
3  <SOAP-ENV:Value>SOAP-ENV:Sender</SOAP-ENV:Value>
4  <SOAP-ENV:Subcode>
5  <SOAP-ENV:Value>ter:InvalidArgs</SOAP-ENV:Value>
6  <SOAP-ENV:Subcode>
7  <SOAP-ENV:Value>ter:NoProfile </SOAP-ENV:Value>
8  </SOAP-ENV:Subcode>
9  </SOAP-ENV:Subcode>
10  </SOAP-ENV:Code>
11  <SOAP-ENV:Reason>
12  <SOAP-ENV:Text xml:lang="en">The requested profile token ProfileToken does not exist.</SOAP-ENV:Text>
13  </SOAP-ENV:Reason>
14 </SOAP-ENV:Fault>

Service operations implementation in a Web service application can return various SOAP Faults customized in this way.