Web Service Development Guide : PART I Getting Started : Chapter 6 Working with Data : Complex Data
Complex Data
For WSDL files that include or import an XML schema defining complex types, the code generator creates C++ classes to represent each complex type with relevant accessors and mutators to access the data.
For detailed information on these generated datatype classes and how to marshal and unmarshal them or otherwise manipulate them, please see the HydraExpress XML Binding Development Guide. This section focuses only on how to work with this data in a HydraExpress Web service.
This discussion is based on the provided WeatherSummary example. This example is not discussed in detail in this section other than to observe the data. For more information on this example and on the message patterns it illustrates, please see Chapter 10, “Implementing Solicit-Response and Notification Services.”
Other examples that work with complex data are Fault and MIME. All examples are located at <installdir>\examples\webservices\.
The WSDL and Schema
A schema can be a standalone document (mySchema.xsd), or can be embedded in a WSDL file in a <types> element.
In this excerpt from the WeatherSummary.wsdl, the message weatherUpdate has a part, weatherData, whose type is a WeatherSummary, used in the one-way operation weatherUpdate. (The fact that the operation is one-way rather than the more common request-response is not relevant to this discussion. For more information on one-way operations, see Chapter 9.)
 
...
 
<message name="weatherUpdate">
<part name="weatherData" type="wsx:WeatherSummary"/>
</message>
 
..
 
<!-- One-way -->
<operation name="weatherUpdate">
<input message="tns:weatherUpdate"/>
</operation>
...
The type WeatherSummary is complex and must therefore be defined elsewhere in the WSDL in a types element, or in a referenced schema. The WeatherSummary.wsdl file embeds the schema rather than referencing an external file, like so:
 
<types>
<xsd:schema
targetNamespace="http://www.roguewave.com/rwsf/webservice/examples/WeatherSummary.xsd">
<xsd:complexType name="WeatherSummary">
<xsd:sequence>
<xsd:element name="zipcode" type="xsd:string"/>
<xsd:element name="windSpeed" type="xsd:unsignedInt"/>
<xsd:element name="sky" type="xsd:string"/>
<xsd:element name="temp" type="xsd:int"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</types>
Note that the WeatherSummary complex type contains four simple types, zipcode, windSpeed, sky, and temp.
The Generated Data Classes
To see the code generated for the WeatherSummary.wsdl, ensure that your environment is set up as discussed in Chapter 2, “Setup,” in the HydraExpress User Guide, open a command prompt, and navigate to the directory <installdir>\examples\webservicesug\WeatherSummary.
The command:
 
prompt> rwsfgen example-project.xml
generates code into a code generation directory WeatherSummaryExample. Because the HydraExpress project file example-project.xml points to a WSDL file that contains a schema, the generated project structure will include data subdirectories as well as a relevant namespace directory, as illustrated in Table 3.
In order to simplify this discussion, this table does not list the generated client and server classes, but only the datatype classes.
Table 3 – Relevant files generated for WeatherSummary.wsdl 
Directory
Files
Description
WeatherSummaryExample/
WeatherSummaryExample.xml
Project file defining all project elements. See “The Generated Project Directory”.
    app/
makefile
makefile_debug
Makefiles for app directory. Overwrite protected.
        /data
WeatherSummary_types_main.cpp
WeatherSummary_types_main.vcproj
Sample application file.Overwrite protected.
MSVC project file (Windows only)
    codegen/
makefile
makefile_debug
Makefiles for codegen directory. Overwrite protected.
        /data/
makefile
makefile_debug
Makefiles for data directory.
Overwrite protected.
            /wsx
WeatherSummary.cpp
WeatherSummaryMarshal.cpp
Source files for the datatype classes sorted into separate namespace directories
    docs/
index.html
HTML documentation files
    include/
 
 
        /WeatherSummaryExample
subdirectory projectname
 
WeatherSummary_typesConverter.h
Conversion utility class header file (to convert the simple types defined in the datamap to and from the underlying string class, string)
            wsx
WeatherSummary.h
WeatherSummaryMarshal.h
Header files for the datatype classes
The docs directory contains HTML documentation. Single-click access is provided through the index.html file in the docs directory.
The generator creates one data type class named WeatherSummary, based on the complexType’s name attribute. The source file for class WeatherSummary is placed into the codegen\data\wsx directory, where wsx is the namespace. The data type header file is placed into the include\WeatherSummary\wsx directory where WeatherSummary is the project name.
Class WeatherSummary provides accessors to work with the data, and also includes unmarshal() methods that populate an instance of the class from a serialized WeatherSummary type element, and marshal() methods for converting an instance of the class to a serialized format. These methods pass the marshaling and unmarshaling logic to the generated external marshaling class, WeatherSummaryMarshal. This class contains the logic to unmarshal and marshal the data and is called by WeatherSummary’s unmarshal() and marshal() methods. Unless you are customizing the marshaling and unmarshaling process in some way, you do not need to explicitly use the marshaling class.
The directory app\data contains the sample application for manipulating datatypes (“The Data “main” Sample Application”), and the directory include contains relevant include and header files.
NOTE >> Source code other than that in the app directory is not overwrite protected. You should never add business logic or make any other changes to the generated source code, as these changes will be lost the next time the code is generated.
Working with the Generated Data Classes in the Client and Server Implementations
Let’s take a look at the service operation method in the generated client implementation, WeatherSummaryClient.cpp:
 
void invoke_weatherUpdate(WeatherSummaryProxy& proxy)
{
wsx::WeatherSummary weatherData_in;
rwsf::CallInfo callInfo;
...
Note that the generated code simply creates an instance of the datatype class WeatherSummary. The provided client implementation instantiates a WeatherSummary object, then uses the generated mutators to set a value on each simple type contained in the object:
 
...
{
wsx::WeatherSummary ws;
ws.setSky("overcast");
ws.setTemp(54);
ws.setWindSpeed(12);
ws.setZipcode("97584");
proxy.weatherUpdate(ws);
}
The server-side implementation simply calls the weatherNotification notification-style service (See Chapter 9 for an explanation of the notification message pattern) with the WeatherSummary object. We can look at the weatherNotification operation to understand how to work with the data in WeatherSummary. Here's the generated server implementation:
 
virtual void weatherNotification(rwsf::CallInfo& info,
const wsx::WeatherSummary& weatherData_in);
The provided notification implementation WeatherSummaryNotificationImp.cpp uses the generated accessors to retrieve each simple type’s value:
 
void
WeatherSummaryNotificationImp::weatherNotification(rwsf::CallInfo& callInfo,
const wsx::WeatherSummary& weatherData_in)
{
std::cout << "WEATHER UPDATE RECEIVED: " << std::endl
<< " zipcode = " << weatherData_in.getZipcode() << std::endl
<< " windSpeed = " << weatherData_in.getWindSpeed() << std::endl
<< " sky = " << weatherData_in.getSky() << std::endl
<< " temp = " << weatherData_in.getTemp() << std::endl
<< std::endl;
}
The Data “main” Sample Application
HydraExpress generates a sample application that you can use if you need to manipulate the data or marshal and unmarshal your code. This application is called WeatherSummary_types_main.cpp and is located in the directory WeatherSummaryExample\app\data. Edit this file to replace the TODO items with your application logic.
 
int main()
{
// TODO: Instantiation of generated object here
 
// Use a C++ try block to handle errors that can occur when unmarshalling XML
try {
// TODO: invoke unmarshal method on generated object
} catch (const rwsf::XmlParseException &e) {
std::cerr << "Error unmashaling : " << e.what() << std::endl;
return 1;
} catch (const rwsf::Exception &e) {
std::cerr << "Error : " << e.what() << std::endl;
return 1;
} catch (...) {
std::cerr << "Unexpected Error" << std::endl;
return 1;
}
 
// TODO: manipulate tree here
 
// TODO: invoke marshal method on generated object to create XML from tree }
 
return 0;
}
 
The example WeatherSummary does not ship with an implemented sample main application, as its primary focus is to illustrate various aspects of developing Web services.
For examples that do include implemented main applications, see any example in the <installdir>\examples\xmlbinding directory.