[11].gSOAP 序列化和反序列化的规则

11  gSOAP Serialization and Deserialization Rules

This section describes the serialization and deserialization of C and C++ data types for SOAP 1.1 and 1.2 compliant encoding and decoding.

11.1  SOAP RPC Encoding Versus Document/Literal and xsi:type Info

The wsdl2h tool automatically generates a header file specialized for SOAP RPC encoding or document/literal style. The serialization and deserialization rules for C/C++ objects is almost identical for these styles, except for the following important issues. With SOAP RPC encoding style, care must be taken to ensure typed messages are produced for interoperability and compatibility reasons. To ensure that the gSOAP engine automatically generates typed (xsi:type attributed) messages, use soapcpp2 option -t, see also Section 9.1. While gSOAP can handle untyped messages, some toolkits fail to find deserializers when the xsi:type information is absent. When starting the development of a gSOAP application from a header file, the soapcpp2 compiler will generate WSDL and schema files for SOAP 1.1 document/literal style by default (use the //gsoap directives to control this, see Section 19.2). Use soapcpp2 options -2, -e, and -t to generate code for SOAP 1.2, RPC encoding, and typed messages. With SOAP RPC encoding, generic complexTypes with maxOccurs="unbounded" are not allowed and SOAP encoded arrays must be used. Also XML attributes and unions (XML schema choice) are not allowed with SOAP RPC encoding. Also with SOAP RPC encoding, multi-reference accessors are common to encode co-referenced objects and object digraphs. Multi-reference encoding is not supported in document/literal style, which means that cyclic object digraphs cannot be serialized (the engine will crash). Also DAGs are represented as XML trees in document/literal style messaging.

11.2  Primitive Type Encoding

The default encoding rules for the primitive C and C++ data types are given in the table below:

Type XSD Type
bool boolean
char* (C string) string
char byte
long double decimal (with #import "custom/long_double.h")
double double
float float
int int
long long
LONG64 long
long long long
short short
time_t dateTime
struct tm dateTime (with #import "custom/struct_tm.h")
unsigned char unsignedByte
unsigned int unsignedInt
unsigned long unsignedLong
ULONG64 unsignedLong
unsigned long long unsignedLong
unsigned short unsignedShort
wchar_t* string


Objects of type void and void* cannot be encoded. Enumerations and bit masks are supported as well, see 11.4.

11.3  How to Represent Primitive C/C++ Types as XSD Types

By default, encoding of the primitive types will take place as per SOAP encoding style. The encoding can be changed to any XML Schema type (XSD type) with an optional namespace prefix by using a typedef in the header file input to the gSOAP soapcpp2 tool. The declaration enables the implementation of built-in XML Schema types (also known as XSD types) such as positiveInteger, xsd:anyURI, and xsd:date for which no built-in data structures in C and C++ exist but which can be represented using standard data structures such as strings, integers, and floats. The typedef declaration is frequently used for convenience in C. A typedef declares a type name for a (complex) type expression. The type name can then be used in other declarations in place of the more complex type expression, which often improves the readability of the program code. The gSOAP soapcpp2 compiler interprets typedef declarations the same way as a regular C compiler interprets them, i.e. as types in declarations. In addition however, the gSOAP soapcpp2 compiler will also use the type name in the encoding of the data in SOAP. The typedef name will appear as the XML element name of an independent element and as the value of the xsi:type attribute in the SOAP payload. Many built-in primitive and derived XSD types such as xsd:anyURI, positiveInteger, and decimal can be stored by standard primitive data structures in C++, such as strings, integers, floats, and doubles. To serialize strings, integers, floats, and doubles as built-in primitive and derived XSD types, a typedef declaration can be used to declare an XSD type. For example, the declaration

typedef unsigned int xsd__positiveInteger;


creates a named type positiveInteger which is represented by unsigned int in C++. For example, the encoding of a positiveInteger value 3 is

<positiveInteger xsi:type="xsd:positiveInteger">3</positiveInteger>


The built-in XML Schema datatype hierarchy from the XML Schema Part 2 documentation http://www.w3.org/TR/xmlschema-2 is depicted below.

[11].gSOAP 序列化和反序列化的规则
Figure 1: Built-in Datatype Hierarchy

The built-in primitive and derived numerical XML Schema types are listed below together with their recommended typedef declarations. Note that the SOAP encoding schemas for primitive types are derived from the built-in XML Schema types, so SOAP_ENC__ can be used as a namespace prefix instead of xsd__.

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;
xsd:base64Binary
Represents Base64-encoded arbitrary binary data. For using the xsd:base64Binary XSD Schema type, the use of the base64Binary representation of a dynamic array is strongly recommended, see Section  11.12. However, the type can also be declared as a string and the encoding will be string-based:

typedef char *xsd__base64Binary;

With this approach, 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.
xsd:boolean
For declaring an xsd:boolean XSD Schema type, the use of a bool is strongly recommended. If a pure C compiler is used that does not support the bool type, see Section  11.4.5. The corresponding type declaration is:

typedef bool xsd__boolean;

Type xsd__boolean declares a Boolean (0 or 1), which is encoded as

<xsd:boolean xsi:type="xsd:boolean">...</xsd:boolean>
xsd:byte
Represents a byte (-128...127). The corresponding type declaration is:

typedef char xsd__byte;

Type xsd__byte declares a byte which is encoded as

<xsd:byte xsi:type="xsd:byte">...</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 Schema 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. Strings ( char*) can be used to store xsd:dateTime XSD Schema types. The type declaration is:

typedef char *xsd__dateTime;

In this case, it is up to the application to read and set the dateTime representation.
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 Schema types. The type declaration is:

typedef char *xsd__date;
xsd:decimal
Represents arbitrary precision decimal numbers. It is recommended to use the double type to store xsd:decimal XSD Schema types and the type declaration is:

typedef double xsd__decimal;

Type xsd__decimal declares a double floating point number which is encoded as

<xsd:double xsi:type="xsd:decimal">...</xsd:double>
xsd:double
Corresponds to the IEEE double-precision 64-bit floating point type. The type declaration is:

typedef double xsd__double;

Type xsd__double declares a double floating point number which is encoded as

<xsd:double xsi:type="xsd: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 Schema types. The type declaration is:

typedef char *xsd__duration;
xsd:float
Corresponds to the IEEE single-precision 32-bit floating point type. The type declaration is:

typedef float xsd__float;

Type xsd__float declares a floating point number which is encoded as

<xsd:float xsi:type="xsd: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 (whose binary representation is 111110110111. For using the xsd:hexBinary XSD Schema type, the use of the hexBinary representation of a dynamic array is strongly recommended, see Section  11.13. However, the type can also be declared as a string and the encoding will be string-based:

typedef char *xsd__hexBinary;

With this approach, it is solely the responsibility of the application to make sure the string content consists of a sequence of octets.
xsd:int
Corresponds to a 32-bit integer in the range -2147483648 to 2147483647. If the C++ compiler supports 32-bit int types, the type declaration can use the int type:

typedef int xsd__int;

Otherwise, the C++ compiler supports 16-bit int types and the type declaration should use the long type:

typedef long xsd__int;

Type xsd__int declares a 32-bit integer which is encoded as

<xsd:int xsi:type="xsd:int">...</xsd:int>
xsd:integer
Corresponds to an unbounded integer. Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:

typedef long long xsd__integer;

Type xsd__integer declares a 64-bit integer which is encoded as an unbounded xsd:integer:

<xsd:integer xsi:type="xsd:integer">...</xsd:integer>

Another possibility is to use strings to represent unbounded integers and do the translation in code.
xsd:long
Corresponds to a 64-bit integer in the range -9223372036854775808 to 9223372036854775807. The type declaration is:

typedef long long xsd__long;

Or in Visual C++:

typedef LONG64 xsd__long;

Type xsd__long declares a 64-bit integer which is encoded as

<xsd:long xsi:type="xsd:long">...</xsd:long>
xsd:negativeInteger
Corresponds to a negative unbounded integer ( < 0). Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:

typedef long long xsd__negativeInteger;

Type xsd__negativeInteger declares a 64-bit integer which is encoded as a xsd:negativeInteger:

<xsd:negativeInteger xsi:type="xsd:negativeInteger">...</xsd:negativeInteger>

Another possibility is to use strings to represent unbounded integers and do the translation in code.
xsd:nonNegativeInteger
Corresponds to a non-negative unbounded integer ( > 0). Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:

typedef unsigned long long xsd__nonNegativeInteger;

Type xsd__nonNegativeInteger declares a 64-bit unsigned integer which is encoded as a non-negative unbounded xsd:nonNegativeInteger:

<xsd:nonNegativeInteger xsi:type="xsd:nonNegativeInteger">...</xsd:nonNegativeInteger>

Another possibility is to use strings to represent unbounded integers and do the translation in code.
xsd:nonPositiveInteger
Corresponds to a non-positive unbounded integer ( ≤ 0). Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:

typedef long long xsd__nonPositiveInteger;

Type xsd__nonPositiveInteger declares a 64-bit integer which is encoded as a xsd:nonPositiveInteger:

<xsd:nonPositiveInteger xsi:type="xsd:nonPositiveInteger">...</xsd:nonPositiveInteger>

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:normalizeString XSD Schema types. The type declaration is:

typedef char *xsd__normalizedString;

Type xsd__normalizedString declares a string type which is encoded as

<xsd:normalizedString xsi:type="xsd:normalizedString">...</xsd:normalizedString>

It is solely the responsibility of the application to make sure the strings do not contain carriage return (#xD), line feed (#xA) and tab (#x9) characters.
xsd:positiveInteger
Corresponds to a positive unbounded integer ( ≥ 0). Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:

typedef unsigned long long xsd__positiveInteger;

Type xsd__positiveInteger declares a 64-bit unsigned integer which is encoded as a xsd:positiveInteger:

<xsd:positiveInteger xsi:type="xsd:positiveInteger">...</xsd:positiveInteger>

Another possibility is to use strings to represent unbounded integers and do the translation in code.
xsd:short
Corresponds to a 16-bit integer in the range -32768 to 32767. The type declaration is:

typedef short xsd__short;

Type xsd__short declares a short 16-bit integer which is encoded as

<xsd:short xsi:type="xsd:short">...</xsd:short>
xsd:string
Represents character strings. The type declaration is:

typedef char *xsd__string;

Type xsd__string declares a string type which is encoded as

<xsd:string xsi:type="xsd:string">...</xsd:string>

The type declaration for wide character strings is:

typedef wchar_t *xsd__string;

Both type of strings can be used at the same time, but requires one typedef name to be changed by appending an underscore which is invisible in XML. For example:

typedef wchar_t *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 Schema types. The type declaration is:

typedef char *xsd__time;
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 Schema types. The type declaration is:

typedef char *xsd__token;

Type xsd__token declares a string type which is encoded as

<xsd:token xsi:type="xsd:token">...</xsd:token>

It is solely the responsibility of the application to make sure the strings 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.
xsd:unsignedByte
Corresponds to an 8-bit unsigned integer in the range 0 to 255. The type declaration is:

typedef unsigned char xsd__unsignedByte;

Type xsd__unsignedByte declares a unsigned 8-bit integer which is encoded as

<xsd:unsignedByte xsi:type="xsd:unsignedByte">...</xsd:unsignedByte>
xsd:unsignedInt
Corresponds to a 32-bit unsigned integer in the range 0 to 4294967295. If the C++ compiler supports 32-bit int types, the type declaration can use the int type:

typedef unsigned int xsd__unsignedInt;

Otherwise, the C++ compiler supports 16-bit int types and the type declaration should use the long type:

typedef unsigned long xsd__unsignedInt;

Type xsd__unsignedInt declares an unsigned 32-bit integer which is encoded as

<xsd:unsignedInt xsi:type="xsd:unsignedInt">...</xsd:unsignedInt>
xsd:unsignedLong
Corresponds to a 64-bit unsigned integer in the range 0 to 18446744073709551615. The type declaration is:

typedef unsigned long long xsd__unsignedLong;

Or in Visual C++:

typedef ULONG64 xsd__unsignedLong;

Type xsd__unsignedLong declares an unsigned 64-bit integer which is encoded as

<xsd:unsignedLong xsi:type="xsd:unsignedLong">...</xsd:unsignedLong>
xsd:unsignedShort
Corresponds to a 16-bit unsigned integer in the range 0 to 65535. The type declaration is:

typedef unsigned short xsd__unsignedShort;

Type xsd__unsginedShort declares an unsigned short 16-bit integer which is encoded as

<xsd:unsignedShort xsi:type="xsd:unsignedShort">...</xsd:unsignedShort>

Other XSD Schema types such as gYearMonth, gYear, gMonthDay, gDay, xsd:gMonth, QName, NOTATION, etc., can be encoded similarly using a typedef declaration.

11.3.1  How to Use Multiple C/C++ Types for a Single Primitive XSD Type

Trailing underscores (see Section 10.3) can be used in the type name in a typedef to enable the declaration of multiple storage formats for a single XML Schema type. For example, one part of a C/C++ application's data structure may use plain strings while another part may use wide character strings. To enable this simultaneous use, declare:

typedef char *xsd__string;
typedef wchar_t *xsd__string_;


Now, the xsd__string and xsd__string_ types will both be encoded and decoded as XML string types and the use of trailing underscores allows multiple declarations for a single XML Schema type.

11.3.2  How to use C++ Wrapper Classes to Specify Polymorphic Primitive Types

SOAP 1.1 supports polymorphic types, because XSD Schema types form a hierarchy. The root of the hierarchy is called xsd:anyType (xsd:ur-type in the older 1999 schema). So, for example, an array of xsd:anyType in SOAP may actually contain any mix of element types that are the derived types of the root type. The use of polymorphic types is indicated by the WSDL and schema descriptions of a Web service and can therefore be predicted/expected for each particular case. On the one hand, the typedef construct provides a convenient way to associate C/C++ types with XML Schema types and makes it easy to incorporate these types in a (legacy) C/C++ application. However, on the other hand the typedef declarations cannot be used to support polymorphic XML Schema types. Most SOAP clients and services do not use polymorphic types. In case they do, the primitive polymorphic types can be declared as a hierarchy of C++ classes that can be used simultaneously with the typedef declarations. The general form of a primitive type declaration that is derived from a super type is:

class xsd__type_name: [public xsd__super_type_name]
{ public: Type __item;
   [public:] [private] [protected:]
   method1;
   method2;
   ...
};


where Type is a primitive C type. The __item field MUST be the first field in this wrapper class. For example, the XML Schema type hierarchy can be copied to C++ with the following declarations:

class xsd__anyType { };
class xsd__anySimpleType: public xsd__anyType { };
typedef char *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 char *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 char *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 char *xsd__time;
class xsd__time_: public xsd__anySimpleType { public: xsd__time __item; };
typedef char *xsd__decimal;
class xsd__decimal_: public xsd__anySimpleType { public: xsd__decimal __item; };
typedef char *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 char *xsd__nonPositiveInteger;
class xsd__nonPositiveInteger_: public xsd__integer_ { public: xsd__nonPositiveInteger __item; };
typedef char *xsd__negativeInteger;
class xsd__negativeInteger_: public xsd__nonPositiveInteger_ { public: xsd__negativeInteger __item; };
typedef char *xsd__nonNegativeInteger;
class xsd__nonNegativeInteger_: public xsd__integer_ { public: xsd__nonNegativeInteger __item; };
typedef char *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__unsginedLong_ { 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 char *xsd__string;
class xsd__string_: public xsd__anySimpleType { public: xsd__string __item; };
typedef char *xsd__normalizedString;
class xsd__normalizedString_: public xsd__string_ { public: xsd__normalizedString __item; };
typedef char *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. Only the most frequently used built-in schema types are shown. It is also allowed to include the xsd:base64Binary and xsd:hexBinary types in the hierarchy:

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 11.12 and 11.13. Methods are allowed to be added to the classes above, such as constructors and getter/setter methods, see Section 11.6.4. Wrapper structs are supported as well, similar to wrapper classes. But they cannot be used to implement polymorphism. Rather, the wrapper structs facilitate the use of XML attributes with a primitive typed object, see 11.6.7.

11.3.3  XSD Schema Type Decoding Rules

The decoding rules for the primitive C and C++ data types is given in the table below:

Type Allows Decoding of Precision Lost?
bool [xsd:]boolean no
char* (C string) any type, see 11.3.5 no
wchar_t * (wide string) any type, see 11.3.5 no
 
double [xsd:]double no
  [xsd:]float no
  [xsd:]long no
  [xsd:]int no
  [xsd:]short no
  [xsd:]byte no
  [xsd:]unsignedLong no
  [xsd:]unsignedInt no
  [xsd:]unsignedShort no
  [xsd:]unsignedByte no
  [xsd:]decimal possibly
  [xsd:]integer possibly
  [xsd:]positiveInteger possibly
  [xsd:]negativeInteger possibly
  [xsd:]nonPositiveInteger possibly
  [xsd:]nonNegativeInteger possibly
 
float [xsd:]float no
  [xsd:]long no
  [xsd:]int no
  [xsd:]short no
  [xsd:]byte no
  [xsd:]unsignedLong no
  [xsd:]unsignedInt no
  [xsd:]unsignedShort no
  [xsd:]unsignedByte no
  [xsd:]decimal possibly
  [xsd:]integer possibly
  [xsd:]positiveInteger possibly
  [xsd:]negativeInteger possibly
  [xsd:]nonPositiveInteger possibly
  [xsd:]nonNegativeInteger possibly
 
long long [xsd:]long no
  [xsd:]int no
  [xsd:]short no
  [xsd:]byte no
  [xsd:]unsignedLong possibly
  [xsd:]unsignedInt no
  [xsd:]unsignedShort no
  [xsd:]unsignedByte no
  [xsd:]integer possibly
  [xsd:]positiveInteger possibly
  [xsd:]negativeInteger possibly
  [xsd:]nonPositiveInteger possibly
  [xsd:]nonNegativeInteger possibly




Type Allows Decoding of Precision Lost?
long [xsd:]long possibly, if long is 32 bit
  [xsd:]int no
  [xsd:]short no
  [xsd:]byte no
  [xsd:]unsignedLong possibly
  [xsd:]unsignedInt no
  [xsd:]unsignedShort no
  [xsd:]unsignedByte no
 
int [xsd:]int no
  [xsd:]short no
  [xsd:]byte no
  [xsd:]unsignedInt possibly
  [xsd:]unsignedShort no
  [xsd:]unsignedByte no
 
short [xsd:]short no
  [xsd:]byte no
  [xsd:]unsignedShort no
  [xsd:]unsignedByte no
 
char [xsd:]byte no
  [xsd:]unsignedByte possibly
 
unsigned long long [xsd:]unsignedLong no
  [xsd:]unsignedInt no
  [xsd:]unsignedShort no
  [xsd:]unsignedByte no
  [xsd:]positiveInteger possibly
  [xsd:]nonNegativeInteger possibly
 
unsigned long [xsd:]unsignedLong possibly, if long is 32 bit
  [xsd:]unsignedInt no
  [xsd:]unsignedShort no
  [xsd:]unsignedByte no
 
unsigned int [xsd:]unsignedInt no
  [xsd:]unsignedShort no
  [xsd:]unsignedByte no
 
unsigned short [xsd:]unsignedShort no
  [xsd:]unsignedByte no
 
unsigned char [xsd:]unsignedByte no
 
time_t [xsd:]dateTime no(?)
 


Due to limitations in representation of certain primitive C++ types, a possible loss of accuracy may occur with the decoding of certain XSD Schema types as is indicated in the table. The table does not indicate the possible loss of precision of floating point values due to the textual representation of floating point values in SOAP. All explicitly declared XSD Schema encoded primitive types adhere to the same decoding rules. For example, the following declaration:

typedef unsigned long long xsd__nonNegativeInteger;


enables the encoding and decoding of xsd:nonNegativeInteger XSD Schema types (although decoding takes place with a possible loss of precision). The declaration also allows decoding of xsd:positiveInteger XSD Schema types, because of the storage as a unsigned long long data type.

11.3.4  Multi-Reference Strings

If more than one char pointer points to the same string, the string is encoded as a multi-reference value. Consider for example

char *s = "hello", *t = s;


The s and t variables are assigned the same string, and when serialized, t refers to the content of s:

<string id="123" xsi:type="string">hello</string>
...
<string href="#123"/>


The example assumed that s and t are encoded as independent elements. Note: the use of typedef to declare a string type such as xsd__string will not affect the multi-reference string encoding. However, strings declared with different typedefs will never be considered multi-reference even when they point to the same string. For example

typedef char *xsd__string;
typedef char *xsd__anyURI;
xsd__anyURI *s = "http://www.myservice.com";
xsd__string *t = s;


The variables s and t point to the same string, but since they are considered different types their content will not be shared in the SOAP payload through a multi-referenced string.

11.3.5  "Smart String" Mixed-Content Decoding

The implementation of string decoding in gSOAP allows for mixed content decoding. If the SOAP payload contains a complex data type in place of a string, the complex data type is decoded in the string as plain XML text. For example, suppose the getInfo service operation returns some detailed information. The service operation is declared as:

// Contents of header 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

<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"
<SOAP-ENV:Body>
<getInfoResponse>
<detail>
<picture>Mona Lisa by <i>Leonardo da Vinci</i></picture>
</detail>
</getInfoResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


As a result of the mixed content decoding, the detail string contains "<picture>Mona Lisa by <i>Leonardo da Vinci</i></picture>".

11.3.6  C++ Strings

gSOAP supports C++ strings std::string and std::wstring wide character strings. For example:

typedef std::string xsd__string;
class ns__myClass
{ public:
   xsd__string s; // serialized with xsi:type="xsd:string"
   std::string t; // serialized without xsi:type
...
};


Caution: Please avoid mixing std::string and C strings (char*) in the header file when using SOAP 1.1 encoding. The problem is that multi-referenced strings in SOAP encoded messages cannot be assigned simultaneously to a std::string and a char* string.

11.3.7  Changing the Encoding Precision of float and double Types

The double encoding format is by default set to "%.18G" (see a manual on printf text formatting in C), i.e. at most 18 digits of precision to limit a loss in accuracy. The float encoding format is by default "%.9G", i.e. at most 9 digits of precision. The encoding format of a double type can be set by assigning a format string to soap.double_format, where soap is a variable that contains the current runtime context. For example:

struct soap soap;
soap_init(&soap); // sets double_format = "%.18G"
soap.double_format = "%e"; // redefine


which causes all doubles to be encoded in scientific notation. Likewise, the encoding format of a float type can be set by assigning a format string to the static 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 encoded with four digits precision. Caution: The format strings are not automatically reset before or after SOAP communications. An error in the format string may result in the incorrect encoding of floating point values.

11.3.8  INF, -INF, and NaN Values of float and double Types

The gSOAP runtime stdsoap2.cpp and header file stdsoap2.h support the marshalling of IEEE INF, -INF, and NaN representations. Under certain circumstances this may break if the hardware and/or C/C++ compiler does not support these representations. To remove the representations, remove the inclusion of the < math.h > header file from the stdsoap2.h file. You can control the representations as well, which are defined by the macros:

#define FLT_NAN
#define FLT_PINFTY
#define FLT_NINFTY
#define DBL_NAN
#define DBL_PINFTY
#define DBL_NINFTY

 

11.4  Enumeration Serialization

Enumerations are generally useful for the declaration of named integer-valued constants, also called enumeration constants.

11.4.1  Serialization of Symbolic Enumeration Constants

The gSOAP 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

<weekday xsi:type="weekday">Mon</weekday>


The value of the xsi:type attribute is the enumeration-type identifier's name. If the element is independent as in the example above, the element name is the enumeration-type identifier's name. The encoding of complex types such as enumerations requires a reference to an XML Schema through the use of a namespace prefix. The namespace prefix can be specified as part of the enumeration-type identifier's name, with the usual namespace prefix conventions for identifiers. This can be used to explicitly specify the encoding style. For example:

enum ns1__weekday {Mon, Tue, Wed, Thu, Fri, Sat, Sun};


The enumeration-constant Sat, for example, is encoded as:

<ns1:weekday xsi:type="ns1:weekday">Sat</ns1:weekday>


The corresponding XML Schema for this enumeration data type would be:

<xsd:element name="weekday" type="tns:weekday"/>
<xsd:simpleType name="weekday">
   <xsd:restriction base="xsd:string">
      <xsd:enumeration value="Mon"/>
      <xsd:enumeration value="Tue"/>
      <xsd:enumeration value="Wed"/>
      <xsd:enumeration value="Thu"/>
      <xsd:enumeration value="Fri"/>
      <xsd:enumeration value="Sat"/>
      <xsd:enumeration value="Sun"/>
   </xsd:restriction>
</xsd:simpleType>

 

11.4.2  Encoding of Enumeration Constants

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 ns1__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:

<ns1:workday xsi:type="ns1:workday">5</ns1:workday>


Since this is legal in C++ and SOAP allows enumeration constants to be integer literals, this method ensures that non-symbolic enumeration constants are correctly communicated to another party if the other party accepts literal enumeration constants (as with the gSOAP soapcpp2 tool). Both symbolic and literal enumeration constants can be decoded. To enforce the literal enumeration constant encoding and to get the literal constants in the WSDL file, use the following trick:

enum ns1__nums { _1 = 1, _2 = 2, _3 = 3 };


The difference with an enumeration type without a list of values and the enumeration type above is that the enumeration constants will appear in the WSDL service description.

11.4.3  Initialized Enumeration Constants

The gSOAP soapcpp2 compiler supports the initialization of enumeration constants, as in:

enum ns1__relation {LESS = -1, EQUAL = 0, GREATER = 1};


The symbolic names LESS, EQUAL, and GREATER will appear in the SOAP payload for the encoding of the ns1__relation enumeration values -1, 0, and 1, respectively.

11.4.4  How to "Reuse" Symbolic Enumeration Constants

A well-known deficiency of C and C++ enumeration types is the lack of support for the reuse of symbolic names by multiple enumerations. That is, the names of all the symbolic constants defined by an enumeration cannot be reused by another enumeration. To force encoding of the same symbolic name by different enumerations, the identifier of the symbolic name can end in an underscore (_) or any number of underscores to distinguish it from other symbolic names in C++. This guarantees that the SOAP encoding will use the same name, while the symbolic names can be distinguished in C++. Effectively, the underscores are removed from a symbolic name prior to encoding. Consider for example:

enum ns1__workday {Mon, Tue, Wed, Thu, Fri};
enum ns1__weekday {Mon_, Tue_, Wed_, Thu_, Fri_, Sat_, Sun_};


which will result in the encoding of the constants of enum ns1__weekday without the underscore, for example as Mon. As an alternative to the trailing underscores that can get quite long for commonly used symbolic enum names, you can use the following convention with double underscores to add the enum name to the enum constants:

enum prefixedname { prefixedname__enumconst1, prefixedname__enumconst2, ... };


where the type name of the enumeration prefixedname is a prefixed name, such as:

enum ns1__workday { ns1__workday__Mon, ns1__workday__Tue, ns1__workday__Wed, ns1__workday__Thu, ns1__workday__Fri };
enum ns1__weekday { ns1__workday__Mon, ns1__workday__Tue, ns1__workday__Wed, ns1__workday__Thu, ns1__workday__Fri, ns1__workday__Sat, ns1__workday__Sun };


This ensures that the XML schema enumeration values are still simply Mon, Tue, Wed, Thu, Fri, Sat, and Sun. Caution: The following declaration:

enum ns1__workday {Mon, Tue, Wed, Thu, Fri};
enum ns1__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.

11.4.5  Boolean Enumeration Serialization for C

When developing a C Web service application, the C++ bool type should not be used since it is not usually supported by the C compiler. Instead, an enumeration type should be used to serialize true/false values as xsd:boolean Schema type enumeration values. The xsd:boolean XML Schema type is defined as:

enum xsd__boolean {false_, true_};


The value false_, for example, is encoded as:

<xsd:boolean xsi:type="xsd:boolean">false</xsd:boolean>


Peculiar of the SOAP boolean type encoding is that it only defines the values 0 and 1, while the built-in XML Schema boolean type also defines the false and true symbolic constants as valid values. The following example declaration of an enumeration type lacks named constants altogether to force encoding of the enumeration values as literal constants:

enum SOAP_ENC__boolean {};


The value 0, for example, is encoded with an integer literal:

<SOAP-ENC:boolean xsi:type="SOAP-ENC:boolean">0<SOAP-ENC:boolean>

 

11.4.6  Bitmask Enumeration Serialization

A bitmask is an enumeration of flags such as declared with C#'s [Flags] enum annotation. gSOAP supports bitmask encoding and decoding for interoperability. However, bitmask types are not standardized with SOAP RPC. A special syntactic convention is used in the header file input to the gSOAP soapcpp2 compiler to indicate the use of bitmasks with an asterisk:

enum * name { enum-constant, enum-constant, ... };


The gSOAP soapcpp2 compiler will encode the enumeration constants as flags, i.e. as a series of powers of 2 starting with 1. The enumeration constants can be or-ed to form a bitvector (bitmask) which is encoded and decoded as a list of symbolic values in SOAP. For example:

enum * ns__machineStatus { ON, BELT, VALVE, HATCH};
int ns__getMachineStatus(char *name, char *enum ns__machineStatus result);


Note that the use of the enum does not require the asterisk, only the definition. The gSOAP soapcpp2 compiler generates the enumeration:

enum ns__machineStatus { ON=1, BELT=2, VALVE=4, HATCH=8};


A service operation implementation in a Web service can return:

int ns__getMachineStatus(struct soap *soap, char *name, enum ns__machineStatus result)
{ ...
   *result = BELT - HATCH;
   return SOAP_OK;
}

 

11.5  Struct Serialization

A struct data type is encoded as an XML Schema complexType (SOAP-encoded compound data type) such that the struct name forms the data type's element name and schema type and the fields of the struct are the data type's accessors. This encoding is identical to the class instance encoding without inheritance and method declarations, see Section 11.6 for further details. However, the encoding and decoding of structs is more efficient compared to class instances due to the lack of inheritance and the requirement by the serialization routines to check inheritance properties at run time. Certain type of fields of a struct can be (de)serialized as XML attributes using the @ type qualifier. See Section 11.6.7 for more details. See Section 10.3 for more details on the struct/class member field serialization and the resulting element and attribute qualified forms.

11.6  Class Instance Serialization

A class instance is serialized as an XML Schema complexType (SOAP-encoded compound data type) such that the class name forms the data type's element name and schema type and the data member fields are the data type's accessors. Only the data member fields are encoded in the SOAP payload. Class methods are not encoded. The general form of a class declaration is:

class [namespace_prefix__]class_name1 [:[public:] [private:] [protected:] [namespace_prefix__]class_name2]
{
   [public:] [private:] [protected:]
   field1;
   field2;
   ...
   [public:] [private:] [protected:]
   method1;
   method2;
   ...
};


where

namespace_prefix__
is the optional namespace prefix of the compound data type (see identifier translation rules  10.3)
class_name1
is the element name of the compound data type (see identifier translation rules  10.3).
class_name2
is an optional base class.
field
is a field declaration (data member). A field MAY be declared static and const and MAY be initialized.
method
is a method declaration. A method MAY be declared virtual, but abstract methods are not allowed. The method parameter declarations are REQUIRED to have parameter identifier names.
[public:] [private:] [protected:]
are OPTIONAL. Only members with public acces permission can be serialized.

A class name is REQUIRED to be unique and cannot have the same name as a struct, enum, or service operation name specified in the header file input to the gSOAP soapcpp2 compiler. The reason is that service operation requests are encoded similarly to class instances in SOAP and they are in principle undistinguishable (the method parameters are encoded just as the fields of a class). Only single inheritance is supported by the gSOAP soapcpp2 compiler. Multiple inheritance is not supported, because of the limitations of the SOAP protocol. If a constructor method is present, there MUST also be a constructor declaration with empty parameter list. Classes should be declared "volatile" if you don't want gSOAP to add serialization methods to these classes, see Section 19.4 for more details. Class templates are not supported by the gSOAP soapcpp2 compiler, but you can use STL containers, see Section 11.11.8. You can also define your own containers similar to STL containers. Certain type of fields of a struct can be (de)serialized as XML attributes using the @ type qualifier. See Section 11.6.7 for more details. See Section 10.3 for more details on the struct/class member field serialization and the resulting element and attribute qualified forms. Arrays may be embedded within a class (and struct) using a pointer field and size information, see Section 11.11.7. This defines what is sometimes referred to in SOAP as "generics". Void pointers may be used in a class (or struct), but you have to add a type field so the gSOAP runtime can determine the type of object pointed to, see Section 11.9. A class instance is encoded as:

<[namespace-prefix:]class-name xsi:type="[namespace-prefix:]class-name">
<basefield-name1 xsi:type="...">...</basefield-name1>
<basefield-name2 xsi:type="...">...</basefield-name2>
...
<field-name1 xsi:type="...">...</field-name1>
<field-name2 xsi:type="...">...</field-name2>
...
</[namespace-prefix:]class-name>


where the field-name accessors have element-name representations of the class fields and the basefield-name accessors have element-name representations of the base class fields. (The optional parts resulting from the specification are shown enclosed in [].) The decoding of a class instance allows any ordering of the accessors in the SOAP payload. However, if a base class field name is identical to a derived class field name because the field is overloaded, the base class field name MUST precede the derived class field name in the SOAP payload for decoding. gSOAP guarantees this, but interoperability with other SOAP implementations is cannot be guaranteed.

11.6.1  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;
   ns__Shape();
   ns__Shape(int sides, enum ns__Green color);
   ~ns__Shape();
};


The implementation of the methods of class ns__Shape must not be part of the header file and need to be defined elsewhere. An instance of class ns__Shape with name Triangle, 3 sides, and color Green is encoded as:

<ns:Shape xsi:type="ns:Shape">
<name xsi:type="string">Triangle</name>
<sides xsi:type="int">3</sides>
<color xsi:type="ns:Color">Green</color>
</ns:shape>


The namespace URI of the namespace prefix ns must be defined by a namespace mapping table, see Section 10.4.

11.6.2  Initialized static const Fields

A data member field of a class declared as static const is initialized with a constant value at compile time. This field is encoded in the serialization process, but is not decoded in the deserialization process. For example:

// Contents of file "triangle.h":
class ns__Triangle : public ns__Object
{
   public:
   int size;
   static const int sides = 3;
};


An instance of class ns__Triangle is encoded in SOAP as:

<ns:Triangle xsi:type="ns:Triangle">
<name xsi:type="string">Triangle</name>
<size xsi:type="int">15</size>
<sides xsi:type="int">3>/sides>
</ns:Triangle>


Decoding will ignore the sides field's value. Caution: The current gSOAP implementation does not support encoding static const fields, due to C++ compiler compatibility differences. This feature may be provided the future.

11.6.3  Class Methods

A class declaration in the header file input to the gSOAP soapcpp2 compiler MAY include method declarations. The method implementations MUST NOT be part of the header file but are required to be defined in another C++ source that is externally linked with the application. This convention is also used for the constructors and destructors of the class. Dynamic binding is supported, so a method MAY be declared virtual.

11.6.4  Getter and Setter 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 in the serialization phase from the virtual soap_serialization method generated by the gSOAP soapcpp2 compiler. You can use setter methods to process a class instance just before it is serialized. A setter method can be used to convert application data, such as translating transient application data into serializable data, for example. You can also use setter methods to retrieve dynamic content and use it to update a class instance right before serialization. Remember setters as "set to serialize" operations. Getter methods are invoked after deserialization of the instance. You can use them to adjust the contents of class instances after all their members have been deserialized. Getters can be used to convert deserialized members into transient members and even invoke methods to process the deserialized data on the fly. Getter and setter methods have the following signature:

[virtual] int get(struct soap *soap) [const];
[virtual] int set(struct soap *soap);


The active soap struct 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:
   unsignedchar *__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 struct passed to the set method (you can pass data via the void*soap.user field). 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 11.3.2. 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:

int update::set(struct soap *soap)
{
   this->__item = time(NULL);
   return SOAP_OK;
}


Therefore, serialization results in the inclusion of a time stamp in XML. Caution: a get method is invoked only when the XML element with its data was completely parsed. The method is not invoked when the element is an xsi:nil element or has an href attribute. Caution: The soap_serialize method of a class calls the setter (when provided). However, the soap_serialize method is declared const while the setter should be allowed to modify the contents of the class instance. Therefore, the gSOAP-generated code recasts the instance and the const is removed when invoking the setter.

11.6.5  Streaming XML with Getter and Setter Methods

Getter methods enable streaming XML operations. A getter method is invoked when the object is deserialized and the rest of the SOAP/XML message has not been processed 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 code is shown below:

class h__Authentication
{ public:
   char *id;
   int get(struct soap *soap);
};
class SOAP_ENV__Header
{ public:
   h__Authentication *h__authentication;
};


The Authentication SOAP Header field 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 processed.

11.6.6  Polymorphism, Derived Classes, and Dynamic Binding

Interoperability between client and service applications developed with gSOAP is established even when clients and/or services use derived classes instead of the base classes used in the declaration of the service operation parameters. A client application MAY use pointers to instances of derived classes for the input parameters of a service operation. If the service was compiled with a declaration and implementation of the derived class, the service operation base class input parameters are demarshalled and a derived class instance is created instead of a base class instance. If the service did not include a declaration of the derived class, the derived class fields are ignored and a base class instance is created. Therefore, interoperability is guaranteed even when the client sends an instance of a derived classes and when a service returns an instance of a derived class. The following example declares Base and Derived classes and a service operation that takes a pointer to a Base class instance and returns a Base class instance:

// Contents of file "derived.h"
class Base
{
   public:
   char *name;
   Base();
   virtual void print();
};
class Derived : public Base
{
   public:
   int num;
   Derived();
   virtual void print();
};
int method(Base *in, struct methodResponse { Base *out; } &result);


This header file specification is processed by the gSOAP soapcpp2 compiler to produce the stub and skeleton routines which are used to implement a client and service. The pointer of the service operation is also allowed to point to Derived class instances and these instances will be marshalled as Derived class instances and send to a service, which is in accord to the usual semantics of parameter passing in C++ with dynamic binding. The Base and Derived class method implementations are:

// Method implementations of the Base and Derived classes:
#include "soapH.h"
...
Base::Base()
{
   cout << "created a Base class instance" << endl;
}
Derived::Derived()
{
   cout << "created a Derived class instance" << endl;
}
Base::print()
{
   cout << "print(): Base class instance " << name << endl;
}
Derived::print()
{
   cout << "print(): Derived class instance " << name << " " << num << endl;
}


Below is an example CLIENT application that creates a Derived class instance that is passed as the input parameter of the service operation:

// CLIENT
#include "soapH.h"
int main()
{
   struct soap soap;
   soap_init(&soap);
   Derived obj1;
   Base *obj2;
   struct methodResponse r;
   obj1.name = "X";
   obj1.num = 3;
   soap_call_method(&soap, url, action, &obj1, r);
   r.obj2->print();
}
...


The following example SERVER1 application copies a class instance (Base or Derived class) from the input to the output parameter:

// SERVER1
#include "soapH.h"
int main()
{
   soap_serve(soap_new());
}
int method(struct soap *soap, Base *obj1, struct methodResponse &result)
{
   obj1->print();
   result.obj2 = obj1;
   return SOAP_OK;
}
...


The following messages are produced by the CLIENT and SERVER1 applications:

CLIENT: created a Derived class instance
SERVER1: created a Derived class instance
SERVER1: print(): Derived class instance X 3
CLIENT: created a Derived class instance
CLIENT: print(): Derived class instance X 3


Which indicates that the derived class kept its identity when it passed through SERVER1. Note that instances are created both by the CLIENT and SERVER1 by the demarshalling process. Now suppose a service application is developed that only accepts Base class instances. The header file is:

// Contents of file "base.h":
class Base
{
   public:
   char *name;
   Base();
   virtual void print();
};
int method(Base *in, Base *out);


This header file specification is processed by the gSOAP soapcpp2 tool to produce skeleton routine which is used to implement a service (so the client will still use the derived classes). The method implementation of the Base class are:

// Method implementations of the Base class:
#include "soapH.h"
...
Base::Base()
{
   cout << "created a Base class instance" << endl;
}
Base::print()
{
   cout << "print(): Base class instance " << name << endl;
}


And the SERVER2 application is that uses the Base class is:

// SERVER2
#include "soapH.h"
int main()
{
   soap_serve(soap_new());
}
int method(struct soap *soap, Base *obj1, struct methodResponse &result)
{
   obj1->print();
   result.obj2 = obj1;
   return SOAP_OK;
}
...


Here are the messages produced by the CLIENT and SERVER2 applications:

CLIENT: created a Derived class instance
SERVER2: created a Base class instance
SERVER2: print(): Base class instance X
CLIENT: created a Base class instance
CLIENT: print(): Base class instance X


In this example, the object was passed as a Derived class instance to SERVER2. Since SERVER2 only implements the Base class, this object is converted to a Base class instance and send back to CLIENT.

11.6.7  XML Attributes

The SOAP RPC/LIT and SOAP DOC/LIT encoding styles support XML attributes in SOAP messages while SOAP RPC with "Section 5" encoding does not support XML attributes other than the SOAP and XSD specific attributes. SOAP RPC "Section 5" encoding has advantages for cross-language interoperability and data encodings such as graph serialization. However, RPC/LIT and DOC/LIT enables direct exchange of XML documents, which may include encoded application data structures. Language interoperability is compromised, because no mapping between XML and the typical language data types is defined. The meaning of the RPC/LIT and DOC/LIT XML content is Schema driven rather than application/language driven. gSOAP supports XML attribute (de)serialization of members in structs and classes. Attributes are primitive XSD types, such as strings, enumerations, boolean, and numeric types. To declare an XML attribute in a struct/class, the qualifier @ is used with the type of the attribute. The type must be primitive type (including enumerations and strings), which can be declared with or without a typedef to associate a XSD type with the C/C+ type. For example

typedef char *xsd__string;
typedef bool *xsd__boolean;
enum ns__state { _0, _1, _2 };
struct ns__myStruct
{
   @xsd__string ns__type; // encode as XML attribute 'ns:type' of type 'xsd:string'
   @xsd__boolean ns__flag = false; // encode as XML attribute 'ns:flag' of type 'xsd:boolean'
   @enum ns__state ns__state = _2; // encode as XML attribute 'ns:state' of type 'ns:state'
   struct ns__myStruct *next;
};


The @ qualifier indicates XML attribute encoding for the ns__type, ns__flag, and ns__state fields. Note that the namespace prefix ns is used to distinguish these attributes from any other attributes such as xsi:type (ns:type is not to be confused with xsi:type). Default values can be associated with any field that has a primitive type in a struct/class, as is illustrated in this example. The default values are used when the receiving message does not contain the corresponding values. String attributes are optional. Other type of attributes should be declared as pointers to make them optional:

struct ns__myStruct
{
   @int *a; // omitted when NULL
};


Because a service operation request and response is essentially a struct, XML attributes can also be associated with method requests and responses. For example

int ns__myMethod(@char *ns__name, ...);


Attributes can also be attached to the dynamic arrays, binary types, and wrapper classes/structs of primitive types. Wrapper classes are described in Section 11.3.2. For example

struct xsd__string
{
   char *__item;
   @xsd__boolean flag;
};


and

struct xsd__base64Binary
{
   unsigned char *__ptr;
   int __size;
   @xsd__boolean flag;
};


The attribute declarations MUST follow the __item, __ptr, and __size fields which define the characteristics of wrapper structs/classes and dynamic arrays. Caution: Do not use XML attributes with SOAP RPC encoding. You can only use attributes with RPC literal encoding.

11.6.8  QName Attributes and Elements

gSOAP ensures the proper decoding of XSD QNames. An element or attribute with type QName (Qualified Name) contains a namespace prefix and a local name. You can declare a QName type as a typedef char *xsd__QName. Values of type QName are internally handled as regular strings. gSOAP takes care of the proper namespace prefix mappings when deserializing QName values. For 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 fields are serialized, their string contents are just transmitted (which means that the application is responsible to ensure proper formatting of the QName strings prior to transmission). When the fields are deserialized however, gSOAP takes care mapping the qualifiers to the appropriate namespace prefixes. Suppose that the inbound value for the elt is x:def, where the namespace name associated with the prefix x matches the namespace name of the prefix ns (as defined in the namespace mapping table). Then, the value is automatically converted into ns:def. If the namespace name is not in the table, then x:def is converted to "URI":def where "URI" is the namespace URI bound to x in the message received. This enables an application to retrieve the namespace information, whether it is in the namespace mapping table or not. Note: QName is a pre-defined typedef type and used by gSOAP to (de)serialize SOAP Fault codes which are QName elements.

11.7  Union Serialization

A union is only serialized if the union is used within a struct or class declaration that includes a int __union field that acts as a discriminant or selector for the union fields. The selector stores run-time usage information about the union fields. That is, the selector is used to enumerate the union fields such that the gSOAP engine is able to select the correct union field to serialize. A union within a struct or class with a selector field represents xs:choice within a Schema complexType component. For example:

struct ns__PO
{ ... };
struct 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 is expanded as a xs:choice:

<complexType name="composite">
   <sequence>
      <element name="name" type="xsd:string"/>
      <choice>
         <element name="po" type="ns:PO"/>
         <element name="invoice" type="ns:Invoice"/>
      </choice>
   </sequence>
</complexType>


Therefore, the name of the union and field can be freely chosen. However, the union name should be qualified (as shown in the example) to ensure instances of XML Schemas with elementFormDefault="qualified" are correctly serialized (po and invoice are ns: qualified). The int __union field selector's values are determined by the soapcpp2 compiler. Each union field name has a selector value formed by:

SOAP_UNION_union-name_field-name


These selector values enumerate the union fields starting with 1. The value 0 can be assigned to omit the serialization of the union, but only if explicitly allowed by validation rules, which requires minOccurs="0" for the xs:choice as follows:

struct ns__composite
{
   char *name;
   int __union 0; // <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 field that must be of type int and the field name is no longer relevant:

struct ns__composite
{
   char *name;
   $int select 0; // <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 PO
data.value.po.number = ...; // populate the PO


Note that failing to set the selector to a valid union field can lead to a crash of the gSOAP serializer because it will attempt to serialize an invalid union field. For deserialization of union types, the selector will be set to 0 (when permitted) by the gSOAP deserializer or set to one of the union field selector values as determined by the XML payload. When more than one union is used in a struct or class, the __union selectors must be renamed to avoid name clashes by using suffixes as in:

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_UNIO_ns__Email_or_Fax_[email - fax]
   union ns__Email_or_Fax data;
};

 

11.8  Serializing Pointer Types

The serialization of a pointer to a data type amounts to the serialization of the data type in SOAP and the SOAP encoded representation of a pointer to the data type is indistinguishable from the encoded representation of the data type pointed to.

11.8.1  Multi-Referenced Data

A data structure pointed to by more than one pointer is serialized as SOAP multi-reference data. This means that the data will be serialized only once and identified with a unique id attribute. The encoding of the pointers to the shared data is done through the use of href or ref attributes to refer to the multi-reference data. See Section 9.12 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 (de)serialization use the WITH_NOIDREF compile-time flag to permanently disable id-href processing. Cyclic C/C++ data structures are encoded with multi-reference SOAP encoding. 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:

<ns:list id="1" xsi:type="ns:list">
   <value xsi:type="xsd:string">abc</value>
   <next xsi:type="ns:list">
      <value xsi:type="xsd:string">def</value>
      <next href="#1"/>
   </next>
</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 fields while the other uses non-pointer-based fields:

typedef long xsd__int;
struct ns__record
{
   xsd__int *a;
   xsd__int *b;
} P;
struct ns__record
{
   xsd__int a;
   xsd__int b;
} R;
...
   P.a = &n;
   P.b = &n;
...


Since both a and b fields of P point to the same integer, the encoding of P is multi-reference:

<ns:record xsi:type="ns:record">
   <a href="#1"/>
   <b href="#1"/>
</ns:record>
<id id="1" xsi:type="xsd:int">123</id>


Now, the decoding 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.

11.8.2  NULL Pointers and Nil Elements

A NULL pointer is not serialized, unless the pointer itself is pointed to by another pointer (but see Section 9.12 to control the serialization of NULLs). For example:

struct X
{
   int *p;
   int **q;
}


Suppose pointer q points to pointer p and suppose p=NULL. In that case the p pointer is serialized as

<... id="123" xsi:nil="true"/>


and the serialization of q refers to href="#123". Note that SOAP 1.1 does not support pointer to pointer types (!), so this encoding is specific to gSOAP. The pointer to pointer encoding is rarely used in codes anyway. More common is a pointer to a data type such as a struct with pointer fields. Caution: When the deserializer encounters an XML element that has a xsi:nil="true" attribute but the corresponding 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. The types section of a WSDL description contains information on the "nilability" of data.

11.9  Void Pointers

In general, void pointers (void*) cannot be (de)serialized because the type of data referred to is untyped. To enable the (de)serialization of the void pointers that are members of structs or classes, you can insert a int __type field right before the void pointer field. The int __ type field contains run time information on the type of the data pointed to by void* member in a struct/class to enable the (de)serialization of this data. The int __type field is set to a SOAP_TYPE_X value, where X is the name of a type. gSOAP generates the SOAP_TYPE_X definitions in soapH.h and uses them internally to uniquely identify the type of each object. The type naming conventions outlined in Section 7.5.3 are used to determine the type name for X. Here is an example to illustrate the (de)serialization of a void* field in a struct:

struct myStruct
{
   int __type; // the SOAP_TYPE pointed to by p
   void *p;
};


The __type integer can be set to 0 at run time to omit the serialization of the void pointer field. The following example illustrates the initialization of myStruct with a void pointer to an int:

struct myStruct S;
int n;
S.p = &n;
S.__type = SOAP_TYPE_int;


The serialized output of S contains the integer. The deserializer for myStruct will automatically set the __type field and void pointer to the deserialized data, provided that the XML content for p carries the xsi:type attribute from which gSOAP can determine the type. Important: when (de)serializing strings via a void* field, the void* pointer MUST directly point to the string value rather than indirectly as with all other types. For example:

struct myStruct S;
S.p = (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 fields to handle multiple void pointers in structs/classes. For example

struct myStruct
{
   int __typeOfp; // the SOAP_TYPE pointed to by p
   void *p;
   int __typeOfq; // the SOAP_TYPE pointed to by q
   void *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 11.6.6). For example:

typedef char *xsd__string;
typedef int xsd__int;
typedef float xsd__float;
enum ns__status { on, off };
struct ns__widget { xsd__string name; xsd__int part; }; int ns__myMethod(int __type, void *data, struct ns__myMethodResponse { 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 produced by the gSOAP soapcpp2 compiler declares the polymorphic parameters of type xsd:anyType which is "too loose" and doesn't allow the gSOAP importer to handle the WSDL accurately. Future gSOAP releases might replace xsd:anyType with a choice schema type that limits the choice of types to the types declared in the header file.

11.10  Fixed-Size Arrays

Fixed size arrays are encoded as per SOAP 1.1 one-dimensional array types. Multi-dimensional fixed size arrays are encoded by gSOAP as nested one-dimensional arrays in SOAP. Encoding of fixed size arrays supports partially transmitted and sparse array SOAP formats. The decoding of (multi-dimensional) fixed-size arrays supports the SOAP multi-dimensional array format as well as partially transmitted and sparse array formats. An example:

// Contents of header file "fixed.h":
struct Example
{
   float a[2][3];
};


This specifies a fixed-size array part of the struct Example. The encoding of array a is:

<a xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="float[][2]">
<SOAP-ENC:Array xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="float[3]"
<float xsi:type="float">...</float>
<float xsi:type="float">...</float>
<float xsi:type="float">...</float>
</SOAP-ENC:Array>
<SOAP-ENC:Array xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="float[3]"
<float xsi:type="float">...</float>
<float xsi:type="float">...</float>
<float xsi:type="float">...</float>
</SOAP-ENC:Array>
</a>


Caution: Any decoded parts of a (multi-dimensional) array that do not "fit" in the fixed size array are ignored by the deserializer.

11.11  Dynamic Arrays

As the name suggests, dynamic arrays are much more flexible than fixed-size arrays and dynamic arrays are better adaptable to the SOAP encoding and decoding rules for arrays. In addition, a typical C application allocates a dynamic array using malloc, assigns the location to a pointer variable, and deallocates the array later with free. A typical C++ application allocates a dynamic array using new, assigns the location to a pointer variable, and deallocates the array later with delete. Such dynamic allocations are flexible, but pose a problem for the serialization of data: how does the array serializer know the length of the array to be serialized given only a pointer to the sequence of elements? The application stores the size information somewhere. This information is crucial for the array serializer and has to be made explicitly known to the array serializer by packaging the pointer and array size information within a struct or class.

11.11.1  SOAP Array Bounds Limits

SOAP encoded arrays use the SOAP-ENC:Array type and the SOAP-ENC:arrayType attribute to define the array dimensionality and size. As a security measure to avoid denial of service attacks based on sending a huge array size value 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 100,000 by default. This constant is defined in stdsoap2.h. This constant only affects multi-dimensional arrays and the dimensionality of the receiving array will be lost when the number of elements exceeds 100,000. One-dimensional arrays will be populated in sequential order as expected.

11.11.2  One-Dimensional Dynamic SOAP Arrays

A special form of struct or class is used to define one-dimensional dynamic SOAP-encoded arrays. Each array has a pointer variable and a field that records the number of elements the pointer points to in memory. The general form of the struct declaration that contains a one-dimensional dynamic SOAP-encoded array is:

struct some_name
{
   Type *__ptr; // pointer to array of elements in memory
   int __size; // number of elements pointed to
   [[static const] int __offset [= ...];] // optional SOAP 1.1 array offset
   ... // anything that follows here will be ignored
};


where Type MUST be a type associated with an XML Schema or MUST be a primitive type. If these conditions are not met, a vector-like XML (de)serialization is used (see Section 11.11.7). A primitive type can be used with or without a typedef. If the array elements are structs or classes, then the struct/class type names should have a namespace prefix for schema association, or they should be other (nested) dynamic arrays. An alternative to a struct is to use a class with optional methods that MUST appear after the __ptr and __size fields:

class some_name
{
   public:
   Type *__ptr;
   int __size;
   [[static const] int __offset [= ...];]
   method1;
   method2;
   ... // any fields that follow will be ignored
};


To encode the data type as an array, the name of the struct or class SHOULD NOT have a namespace prefix, otherwise the data type will be encoded and decoded as a generic vector, see Section 11.11.7. The deserializer of a dynamic array can decode partially transmitted and/or SOAP sparse arrays, and even multi-dimensional arrays which will be collapsed into a one-dimensional array with row-major ordering. Caution: SOAP 1.2 does not support partially transmitted arrays. So the __offset field of a dynamic array is ignored.

11.11.3  Example

The following example header file specifies the XMethods Service Listing service getAllSOAPServices service operation and an array of SOAPService data structures:

// Contents of file "listing.h":
class ns3__SOAPService
{
   public:
   int ID;
   char *name;
   char *owner;
   char *description;
   char *homepageURL;
   char *endpoint;
   char *SOAPAction;
   char *methodNamespaceURI;
   char *serviceStatus;
   char *methodName;
   char *dateCreated;
   char *downloadURL;
   char *wsdlURL;
   char *instructions;
   char *contactEmail;
   char *serverImplementation;
};
class ServiceArray
{
   public:
   ns3__SOAPService *__ptr; // points to array elements
   int __size; // number of elements pointed to
   ServiceArray();
   ~ServiceArray();
   void print();
};
int ns__getAllSOAPServices(ServiceArray &return_);


An example client application:

#include "soapH.h" ...
// ServiceArray class method implementations:
ServiceArray::ServiceArray()
{
   __ptr = NULL;
   __size = 0;
}
ServiceArray::~ServiceArray()
{ // destruction handled by gSOAP
}
void ServiceArray::print()
{
   for (int i = 0; i < __size; i++)
      cout << __ptr[i].name << ": " << __ptr[i].homepage << endl;
}
...
// Request a service listing and display results:
{
   struct soap soap;
   ServiceArray result;
   const char *endpoint = "www.xmethods.net:80/soap/servlet/rpcrouter";
   const char *action = "urn:xmethodsServicesManager#getAllSOAPServices";
   ...
   soap_init(&soap);
   soap_call_ns__getAllSOAPServices(&soap, endpoint, action, result);
   result.print();
   ...
   soap_destroy(&soap); // dealloc class instances
   soap_end(&soap); // dealloc deserialized data
   soap_done(&soap); // cleanup and detach soap struct
}

 

11.11.4  One-Dimensional Dynamic SOAP Arrays With Non-Zero Offset

The declaration of a dynamic array as described in 11.11 MAY include an int __offset field. When set to an integer value, the serializer of the dynamic array will use this field as the start index of the array and the SOAP array offset attribute will be used in the SOAP payload. 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 mathematical Vector class, which is a dynamic array of floating point values with an index that starts at 1:

// Contents of file "vector.h":
typedef float xsd__float;
class Vector
{
   xsd__float *__ptr;
   int __size;
   int __offset;
   Vector();
   Vector(int n);
   float& operator[](int i);
}


The implementations of the Vector methods are:

Vector::Vector()
{
   __ptr = NULL;
   __size = 0;
   __offset = 1;
}
Vector::Vector(int n)
{
   __ptr = (float*)malloc(n*sizeof(float));
   __size = n;
   __offset = 1;
}
Vector::~Vector()
{
   if (__ptr)
      free(__ptr);
}
float& Vector::operator[](int i)
{
   return __ptr[i-__offset];
}


An example program fragment that serializes a vector of 3 elements:

struct soap soap;
soap_init(&soap);
Vector v(3);
v[1] = 1.0;
v[2] = 2.0;
v[3] = 3.0;
soap_begin(&soap);
v.serialize(&soap);
v.put("vec");
soap_end(&soap);


The output is a partially transmitted array:

<vec xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:float[4]" SOAP-ENC:offset="[1]">
<item xsi:type="xsd:float">1.0</item>
<item xsi:type="xsd:float">2.0</item>
<item xsi:type="xsd:float">3.0</item>
</vec>


Note that the size of the encoded array is necessarily set to 4 and that the encoding omits the non-existent element at index 0. The decoding of a dynamic array with an __offset field is more efficient than decoding a dynamic array without an __offset field, because the __offset field will be assigned the value of the SOAP-ENC:offset attribute instead of padding the initial part of the array with default values.

11.11.5  Nested One-Dimensional Dynamic SOAP Arrays

One-dimensional 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(int n, int m);
   ~Matrix();
   Vector& operator[](int i);
};


The Matrix type is essentially an array of pointers to arrays which make up the rows of a matrix. The encoding of the two-dimensional dynamic array in SOAP will be in nested form.

11.11.6  Multi-Dimensional Dynamic SOAP Arrays

The general form of the struct declaration for K-dimensional (K > 1) dynamic arrays is:

struct some_name
{
   Type *__ptr;
   int __size[K];
   int __offset[K];
   ... // anything that follows here will be ignored
};


where Type MUST be a type associated with an XML Schema, which means that it must be a typedefed type in case of a primitive type, or a struct/class name with a namespace prefix for schema association, or another dynamic array. If these conditions are not met, a generic vector XML (de)serialization is used (see Section 11.11.7). An alternative is to use a class with optional methods:

class some_name
{
   public:
   Type *__ptr;
   int __size[K];
   int __offset[K];
   method1;
   method2;
   ... // any fields that follow will be ignored
};


In the above, K is a constant denoting the number of dimensions of the multi-dimensional array. To encode the data type as an array, the name of the struct or class SHOULD NOT have a namespace prefix, otherwise the data type will be encoded and decoded as a generic vector, see Section 11.11.7. The deserializer of a dynamic array can decode partially transmitted multi-dimensional arrays. For example, the following declaration specifies a matrix class:

typedef double xsd__double;
class Matrix
{
   public:
   xsd__double *__ptr;
   int __size[2];
   int __offset[2];
};


In contrast to the matrix class of Section 11.11.5 that defined 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 field: __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.

11.11.7  Encoding XML Generics Containing Dynamic Arrays

The XML "generics" concept discussed in the SOAP encoding protocols extends the concept of a SOAP struct by allowing repetitions of elements within the struct. This is just a form of a repetition of XML elements without the SOAP-encoded array requirements. While SOAP-encoded arrays are more expressive (offset information to encode sparse arrays for example), simple repetitions of values are used more frequently. A simple generic reperition is an array-like data structure with a repetition of an element. To achieve this, declare a dynamic array as a struct or class with a name that is qualified with a namespace prefix. SOAP arrays are declared without prefix. For example, we define a Map structure that contains a sequence of pairs of key-val:

struct ns__Map
{
   int __size; // number of pairs
   struct ns__Binding {char *key; char *val;} *pair;
};


Since 2.7.16 it is also possible to use a '$' as a special marker to annotate a size field that must be of type int or size_t and the field name is no longer relevant:

struct ns__Map
{
   $int length; // number of pairs
   struct ns__Binding {char *key; char *val;} *pair;
};


This declares a dynamic array pointed to by pair and size __size. The array will be serialized and deserialized as a sequence of pairs:

<ns:Map xsi:type="ns:Map">
<pair xsi:type="ns:Binding">
<key>Joe</key>
<val>555 77 1234</val>
</pair>
<pair xsi:type="ns:Binding">
<key>Susan</key>
<val>555 12 6725</val>
</pair>
<pair xsi:type="ns:Binding">
<key>Pete</key>
<val>555 99 4321</val>
</pair>
</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. Internal buffering is used by the deserializer to collect the elements. When the end of the list is reached, the buffered elements are copied to a newly allocated space on the heap for the dynamic array. Multiple arrays can be used in a struct/class to support the concept of "generics". Each array results in a repetition of elements in the struct/class. This is achieved with a int __size (or $int) field in the struct/class where the next field (i.e. below the __size field) is a pointer type. The pointer type is assumed to point to an array of values at run time. The __size field holds the number of values at run time. Multiple arrays can be embedded in a struct/class with __size fields that have a distinct names. To make the __size fields distinct, you can end them with a unique name suffix such as __sizeOfstrings, for example. The general convention for embedding arrays is:

struct ns__SomeStruct
{
   ...
   int __sizename1; // number of elements pointed to
   Type1 *field1; // by this field
   ...
   int __sizename2; // number of elements pointed to
   Type2 *field2; // by this field
   ...
};


where name1 and name2 are identifiers used as a suffix to distinguish the __size field. These names can be arbitrary and are not visible in XML. In 2.7.16 and higher this is simplified with a '$' marker:

struct ns__SomeStruct
{
   ...
   $int name1; // number of elements pointed to
   Type1 *field1; // by this field
   ...
   $int name2; // number of elements pointed to
   Type2 *field2; // by this field
   ...
};


For example, the following struct has two embedded arrays:

struct ns__Contact
{
   char *firstName;
   char *lastName;
   $intnPhones; // number of Phones
   ULONG64 *phoneNumber; // array of phone numbers
   $intnEmails; // number of emails
   char **emailAddress; // array of email addresses
   char *socSecNumber;
};


The XML serialization of an example ns__Contact is:

<mycontact xsi:type="ns:Contact">
   <firstName>Joe</firstName>
   <lastName>Smith</lastName>
   <phoneNumber>5551112222</phoneNumber>
   <phoneNumber>5551234567</phoneNumber>
   <phoneNumber>5552348901</phoneNumber>
   <emailAddress>[email protected]</emailAddress>
   <emailAddress>[email protected]</emailAddress>
   <socSecNumber>999999999</socSecNumber>
</mycontact>

 

11.11.8  STL Containers

gSOAP supports the STL containers std::deque, std::list, std::set, and std::vector. STL containers can only be used within classes to declare members that contain multiple values. This is somewhat similar to the embedding of arrays in structs in C as explained in Section 11.11.7, but the STL container approach is more flexible. You need to import stldeque.h, stllist.h, stlset.h, or stlvector.h to enable std::deque, std::list, std::set, and std::vector (de)serialization. Here is an example:

#import "stlvector.h"
class ns__myClass
{ public:
   std::vector < int > *number;
   std::vector < xsd__string > *name;
   ...
};


The use of pointer members is not required but advised. The reason is that interoperability with other SOAP toolkits may lead to copying of ns__ myClass instances at run time when (de)serializing multi-referenced data. When a copy is made, certain parts of the containers will be shared between the copies which could lead to disaster when the classes with their containers are deallocated. Another way to avoid this is to declare class ns__myClass within other data types via a pointer. (Interoperability between gSOAP clients and services does not lead to copying.) The XML Schema that corresponds to the ns__myClass type is

<complexType name="myClass">
   <sequence>
      <element name="number" type="xsd:int" minOccurs="1" maxOccurs="unbounded"/>
      <element name="name" type="xsd:string" minOccurs="1" maxOccurs="unbounded"/>
      ...
   </sequence>
</complexType>


You can specify the minOccurs and maxOccurs values as explained in Section 19.2. You can also implement your own containers similar to STL containers. The containers must be class templates and should define an iterator type, and void clear(), iterator begin(), iterator end(), and iterator insert(iterator pos, const_reference val). The iterator should have a dereference operator to access the container's elements. The dereference operator is used by gSOAP to send a sequence of XML element values. The insert method can be used as a setter method. gSOAP reads a sequence of XML element values and inserts them in the container via this method. Here is in example user-defined container template class:

// simpleVector.h
template  < class T >
class simpleVector
{
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 start;
   iterator finish;
   size_t length;
public:
   simpleVector() { clear(); }
   ~simpleVector() { delete[] start; }
   void\ clear() { start = finish = NULL; }
   iterator begin() { return start; }
   const_iterator begin() const\ { return start; }
   iterator end() { return finish; }
   const_iterator end() const\ { return finish; }
   size_t size() const\ { return finish-start; }
   iterator insert(iterator pos, const_reference val)
      {
         if (!start)
            start = finish = new value_type[length = 4];
         else if (finish > = start + length)
         {
            iterator i = start;
            iterator j = new value_type[2 * length];
            start = j;
            finish = start + length;
            length *= 2;
            if (pos)
               pos = j + (pos - i);
            while (i != finish)
               *j++ = *i++;
         }
         if (pos && pos != finish)
         { iterator i = finish;
            iterator j = i - 1;
            while (j != pos)
               *i− − = *j− −;
         }
         *finish++ = val;
         return pos;
      }
};


To enable the container, we add the following two lines to our gSOAP header file:

#include "simpleVector.h"
template  < class T > class simpleVector;


The container class should not be defined in the gSOAP header file. It must be defined in a separate header file (e.g. "simpleVector.h"). The template  < class T > class simpleVector declaration ensures that gSOAP will recognize simpleVector as a container class. Caution: when parsing XML content the container elements may not be stored in the same order given in the XML content. When gSOAP parses XML it uses the insert container methods to store elements one by one. However, element content that is "forwarded" with href attributes will be appended to the container. Forwarding can take place with multi-referenced data that is referred to from the main part of the SOAP 1.1 XML message to the independent elements that carry ids. Therefore, your application should not rely on the preservation of the order of elements in a container.

11.11.9  Polymorphic Dynamic Arrays and Lists

Polymorphic arrays (arrays of polymorphic element types) can be encoded when declared as an array of pointers to class instances. For example:

class ns__Object
{
   public:
   ...
};
class ns__Data: public ns__Object
{
   public:
   ...
};
class ArrayOfObject
{
   public:
   ns__Object **__ptr; // pointer to array of pointers to Objects
   int __size; // number of Objects pointed to
   int __offset; // optional SOAP 1.1 array offset
};
class ns__Objects
{
   public:
   std::vector < ns__Object* > objects; // vector of pointers to objects
};


The pointers in the array can point to the ns__Object base class or ns__Data derived class instances which will be serialized and deserialized accordingly in SOAP. That is, the array elements are polymorphic. Since we can't use dynamic binding to support polymorphism in C, another mechanism is available based on the serialization of void pointers, that is, dynamic serialization of data referenced by void pointers, see Section 11.9.

struct __wrapper
{
   int __type; // type T represented by SOAP_TYPE_T
   void *__item; // pointer to data of type T
};
struct ArrayOfObject
{
   struct __wrapper __ptr; // pointer to array of pointers to Objects
   int __size; // number of Objects pointed to
   int __offset; // optional SOAP 1.1 array offset
};
struct ns__Objects
{
   int __size;
   struct __wrapper *objects; // array of pointers to wrapped types
};

 

11.11.10  How to Change the Tag Names of the Elements of a SOAP Array or List

The __ptr field in a struct or class declaration of a dynamic array may have an optional suffix part that describes the name of the tags of the SOAP array XML elements. The suffix is part of the field name:

Type *__ptrarray_elt_name


The suffix describes the tag name to be used for all array elements. The usual identifier to XML translations apply, see Section 10.3. The default XML element tag name for array elements is item (which corresponds to the use of field name __ptritem). Consider for example:

struct ArrayOfstring
{
   xsd__string *__ptrstring;    int __size; };


The array is serialized as:

<array xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:string[2]">
<string xsi:type="xsd:string">Hello</string>
<string xsi:type="xsd:string">World</string>
</array>


SOAP 1.1 and 1.2 do not require the use of a specific tag name for array elements. gSOAP will deserialize a SOAP array while ignoring the tag names. Certain XML Schemas used in doc/literal encoding may require the declaration of array element tag names.

11.12  Base64Binary XML Schema Type Encoding

The base64Binary XML Schema type is a special form of dynamic array declared with a pointer (__ptr) to an unsigned char array. For example using a struct:

struct xsd__base64Binary
{
   unsigned char *__ptr;
   int __size;
};


Or with a class:

class xsd__base64Binary
{
   public:
   unsigned char *__ptr;
   int __size;
};


When compiled by the gSOAP soapcpp2 tool, this header file specification will generate base64Binary serializers and deserializers. The SOAP_ENC:base64 encoding is another type for base 64 binary encoding specified by the SOAP data type schema and some SOAP applications may use this form (as indicated by their WSDL descriptions). It is declared by:

struct SOAP_ENC__base64
{
   unsigned char *__ptr;
   int __size;
};


Or with a class:

class SOAP_ENC__base64
{
   unsigned char *__ptr;
   int __size;
};


When compiled by the gSOAP soapcpp2 tool, this header file specification will generate SOAP-ENC:base64 serializers and deserializers. The advantage of using a class is that methods can be used to initialize and manipulate the __ptr and __size fields. The user can add methods to this class to do this. For example:

class xsd__base64Binary
{
   public:
   unsigned char *__ptr;
   int __size;
   xsd__base64Binary(); // Constructor
   xsd__base64Binary(struct soap *soap, int n); // Constructor
   ~xsd__base64Binary(); // Destructor
   unsigned char *location(); // returns the memory location
   int size(); // returns the number of bytes
};


Here are example method implementations:

xsd__base64Binary::xsd__base64Binary()
{
   __ptr = NULL;
   __size = 0;
}
xsd__base64Binary::xsd__base64Binary(struct soap *soap, int n)
{
   __ptr = (unsigned char*)soap_malloc(soap, n);
   __size = n;
}
xsd__base64Binary::~xsd__base64Binary()
{ }
unsigned char *xsd__base64Binary::location()
{
   return __ptr;
}
int xsd__base64Binary::size()
{
   return __size;
}


The following example in C/C++ reads from a raw image file and encodes the image in SOAP using the base64Binary type:

...
FILE *fd = fopen("image.jpg", "rb");
xsd__base64Binary image(&soap, filesize(fd));
fread(image.location(), image.size(), 1, fd);
fclose(fd);
soap_begin(&soap);
image.soap_serialize(&soap);
image.soap_put(&soap, "jpegimage", NULL);
soap_end(&soap);
...


where filesize is a function that returns the size of a file given a file descriptor. Reading the xsd:base64Binary encoded image.

...
xsd__base64Binary image;
soap_begin(&soap);
image.get(&soap, "jpegimage");
soap_end(&soap);
...


The struct or class name soap_enc__base64 should be used for SOAP-ENC:base64 schema type instead of xsd__base64Binary.

11.13  hexBinary XML Schema Type Encoding

The hexBinary XML Schema type is a special form of dynamic array declared with the name xsd__hexBinary and a pointer (__ptr) to an unsigned char array. For example, using a struct:

struct xsd__hexBinary
{
   unsigned char *__ptr;
   int __size;
};


Or using a class:

class xsd__hexBinary
{
   public:
   unsigned char *__ptr;
   int __size;
};


When compiled by the gSOAP soapcpp2 tool, this header file specification will generate base64Binary serializers and deserializers.

11.14  Literal XML Encoding Style

gSOAP supports document/literal encoding by default. Just as with SOAP RPC encoding, literal encoding requires the XML Schema of the message data to be provided e.g. in WSDL in order for the gSOAP soapcpp2 compiler to generate the (de)serialization routines. Alternatively, the optional DOM parser (dom.c and dom++.cpp) can be used to handle generic XML or arbitrary XML documents can be (de)serialized into regular C strings or wide character strings (wchar_t*) by gSOAP (see Section 11.14.1). The //gsoap service encoding, //gsoap service method-encoding, and //gsoap service method-response-encoding directives explicitly enable SOAP encoded or literal encoded messages. For example, to enable RPC encoding style for the entire service, use:

//gsoap ns service encoding: encoded


To enable encoding for particular service methods, use:

//gsoap ns service method-encoding: myMethod encoded
int ns__myMethod(...)


To enable encoding for particular service methods responses when the method request is literal, use:

//gsoap ns service method-response-encoding: myMethod encoded
int ns__myMethod(...)


Instead of the encoded value, you can use literal, or a specific encoding style value. Consider the following example that uses the directive to make the literal encoding explicit. The LocalTimeByZipCode service operation of the LocalTime service provides the local time given a zip code and uses literal encoding (with MS .NET). The following header file declares the method:

int LocalTimeByZipCode(char *ZipCode, char **LocalTimeByZipCodeResult);


Note that none of the data types need to be namespace qualified using namespace prefixes.

//gsoap ns service name: localtime
//gsoap ns service encoding: literal
//gsoap ns service namespace: http://alethea.net/webservices/
int ns__LocalTimeByZipCode(char *ZipCode, char **LocalTimeByZipCodeResult);


In this case, the method name requires to be associated with a schema through a namespace prefix, e.g. ns is used in this example. See Section 19.2 for more details on gSOAP directives. With these directives, the gSOAP soapcpp2 compiler generates client and server sources with the specified settings. The example client program is:

#include "soapH.h"
#include "localtime.nsmap" // include generated map file
int main()
{
   struct soap soap;
   char *t;
   soap_init(&soap);
   if (soap_call_ns__LocalTimeByZipCode(&soap, "http://alethea.net/webservices/LocalTime.asmx", "http://alethea.net/webservices/LocalTimeByZipCode", "32306", &t))
      soap_print_fault(&soap, stderr);
   else
      printf("Time = %s\n", t);
   return 0;
}


To illustrate the manual doc/literal setting, the following client program sets the required properties before the call:

#include "soapH.h"
#include "localtime.nsmap" // include generated map file
int main()
{
   struct soap soap;
   char *t;
   soap_init(&soap);
   soap.encodingStyle = NULL; // don't use SOAP encoding
   soap_set_omode(&soap, SOAP_XML_TREE);" // don't produce multi-ref data (but can accept)
   if (soap_call_ns__LocalTimeByZipCode(&soap, "http://alethea.net/webservices/LocalTime.asmx", "http://alethea.net/webservices/LocalTimeByZipCode", "32306", &t))
      soap_print_fault(&soap, stderr);
   else
      printf("Time = %s\n", t);
   return 0;
}


The SOAP request is:

POST /webservices/LocalTime.asmx HTTP/1.0
Host: alethea.net
Content-Type: text/xml; charset=utf-8
Content-Length: 479
SOAPAction: "http://alethea.net/webservices/LocalTimeByZipCode"

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   <SOAP-ENV:Body>
      <LocalTimeByZipCode xmlns="http://alethea.net/webservices/">
<ZipCode>32306</ZipCode></LocalTimeByZipCode>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 

11.14.1  Serializing and Deserializing Mixed Content XML With Strings

To declare a literal XML "type" to hold XML documents in regular strings, use:

typedef char *XML;


To declare a literal XML "type" to hold XML documents in wide character strings, use:

typedef wchar_t *XML;


Note: only one of the two storage formats can be used. The differences between the use of regular strings versus wide character strings for XML documents are:

  • Regular strings for XML documents MUST hold UTF-8 encoded XML documents. That is, the string MUST contain the proper UTF-8 encoding to exchange the XML document in SOAP messages.
  • Wide character strings for XML documents SHOULD NOT hold UTF-8 encoded XML documents. Instead, the UTF-8 translation is done automatically by the gSOAP runtime marshalling routines.

Here is a C++ example of a service operation specification in which the parameters of the service operation uses literal XML encoding to pass an XML document to a service and back:

typedef char *XML;
ns__GetDocument(XML m__XMLDoc, XML &m__XMLDoc_);


and in C:

typedef char *XML;
ns__GetDocument(XML m__XMLDoc, XML *m__XMLDoc_);


The ns__Document is essentially a struct that forms the root of the XML document. The use of the underscore in the ns__Document response part of the message avoids the name clash between the structs. Assuming that the namespace mapping table contains the binding of ns to http://my.org/ and the binding of m to http://my.org/mydoc.xsd, the XML message is:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:ns="http://my.org/"
   xmlns:m="http://my.org/mydoc.xsd"
   SOAP-ENV:encodingStyle="">
   <SOAP-ENV:Body>
      <ns:GetDocument>
         <XMLDoc xmlns="http://my.org/mydoc.xsd">
            ...
         </XMLDoc>
      </ns:Document>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>


When using literal encoding of method parameters and response as shown in the example above, the literal XML encoding style MUST be specified by setting soap.encodingStyle. For example, to specify no constraints on the encoding style (which is typical) use NULL:

struct soap soap;
soap_init(&soap);
soap.encodingStyle = NULL;


As a result, the SOAP-ENV:encodingStyle attribute will not appear in the SOAP payload. For interoperability with Apache SOAP, use

struct soap soap;
soap_init(&soap);
soap.encodingStyle = "http://xml.apache.org/xml-soap/literalxml";


When the response parameter is an XML type, it will store the entire XML response content but without the enveloping response element. The XML type can be used as part of any data structure to enable the rendering and parsing of custom XML documents. For example:

typedef char *XML;
struct ns__Data /* data in namespace 'ns' */
{
   int number;
   char *name;
   XML m__document; /* XML document in default namespace 'm' */
};
ns__Example(struct ns__Data data, struct ns__ExampleResponse { struct ns__Data data; } *out);

 

你可能感兴趣的:(SOAP)