Web Service Client Class Generator
The Web Service Client Class Generator is provided for automatically creating DataFlex wrapper classes for client web services.
You can load this dialog by selecting the “Client Web Service Class” option from the “Class” tab page in the “Create New” dialog (File / New / Class).
WSDL URL
The class generator requires that you provide it with the location of a web service’s description (its WSDL location). Enter (or paste) the WSDL name into this form. The combo list contains recently entered WSDL URLs.
Parse
Click the Parse button to locate the entered WSDL URL, load it, parse it, and determine how it should be represented as a DataFlex class.
The generator parses the WSDL document and creates a sub-class based on that information. If the parsing was successful, the class is displayed in the Class tab page; the WSDL that was parsed is displayed in the WSDL tab page; a summary of the web service is shown in the Parse Details tab page; a summary of any problems encountered is shown on the Errors / Warnings tab page.
The generator is now ready to create the new class for use in your DataFlex applications.
Generate Class
After the WSDL has been parsed, you may click the Generate Class button. This will create the DataFlex wrapper class for the web service and all additional files required to integrate the class directly in the Studio so that it can be accessed from the Studio’s Class Palette.
A dialog appears that allows you to determine the DataFlex class name and a group name in the controls palette. Normally you should accept the default class and palette defaults. Click the OK button in this dialog to complete the class generation.
Regenerating Classes
You are allowed to regenerate classes. For example, if your server web service changes, you will need to generate the class again. If the generator sees that you are generating a duplicate class, it will warn you and allow you to overwrite the existing class definition.
This is all you need to do to create a web service client. The class is now created and is fully registered within the Studio. At this point, you can choose to create additional clients or you can close the class generator and use the client in your applications.
For more information, refer to Publishing and Using Web Services.
Support Structs with Null Member / Support Simple Types with Null Member
One of the issues we are seeing more of is the mechanism that is used for passing null values in web services. For example, you might have a String that should only be passed when you really want to pass the string. This is usually represented in the schema with minOccurs="0" and maxOccurs="1" definitions. This will be used with simple and complex types:
In the above examples, minOccurs="1" is the default and is implied. This says it is legal to pass 0 or 1 instances of this element.
In the past, with requests, we always passed a single element, which is by definition a valid thing to do. For example, if there was no string to pass, we passed a node with an empty value.
While this is usually technically valid, there really is no way to distinguish between a null and an empty string/value. Most of the time this works and the web service just figures this out. However, lately more web services are considering this to be an error and not processing the request. With responses, this is less of a problem because we can control this. We've always been able to handle an empty node when minOccurs="0" and nothing is passed back to us. In this case, we'd see that the node is missing and assign it an empty value ("" or 0) on our side. This works fine unless you actually need to distinguish between Null and "", which is rare (and not even something we support in our language).
With requests, this is becoming more and more of an issue. This problem occurs with simple types and with structs. The workaround is not simple and it usually involves custom modifying the request XML. This has now been addressed.
As an aside, we explored various ways to auto-detect null values in our structs and datatypes and to not pass them when minOccurs="0". The problem is that we have no reliable way of knowing if a value is meant to be null or an empty value for a type (e.g., "" for a string, 0 for a number, or False for a boolean). The exception has always been dates where we can assume that a 0 date is indeed a null as it is not a valid date - we've always had a way to handle this.
The solution is to provide a way to explicitly mark data as being null. With complex types (structs), this is being done by adding an extra bNull member. With simple types, this is done by defining a "simple type" struct that contains the simple value and the bNull member. The web service then needs to recognize that these bNull members have a special meaning and to properly convert them to and from XML. The Studio's WSDL parser has been altered to create these types of nullable datatypes (when needed) and the cClientWebService class has been changed to support this.
Example
Here is a native simple type:
String sMyValue
Move "John" to sMyValue // this sets the value
// or to mark it null
Move "" to sMyValue // if null make it blank. We don't really know if this is null or blank.
Here is what this would look like using the new nullable struct generated by the web service parser:
Struct tNString
Boolean bNull
String Value
End_Struct
tNString sMyValue
Move "John" to sMyValue.Value // this sets the value
// or to mark it null
Move True to sMyValue.bNull // if null you mark this element as null
The same idea would apply with structs. Consider a regular struct that does not support nulls.
Struct tWSLocalization
String LanguageCode
String LocaleCode
End_Struct
tWSLocalization MyVar
Move "EN" to MyVar.LanguageCode
Move "XX" to MyVar.LocaleCode
// or to mark it null
Move "" to MyVar.LanguageCode // leaving it blank implies it is null - but is it really?
Move "" to MyVar.LocaleCode
Compare this to a nullable struct generated by the Web Service parser:
Struct tWSLocalization
Boolean bNull
tNString LanguageCode
tNString LocaleCode
End_Struct
tWSLocalization MyVar
Move "EN" to MyVar.LanguageCode
Move "XX" to MyVar.LocaleCode
// or to mark it null
Move True to MyVar.bNull // if null, set the element to true
The Studio's client web service generator will now create these null types if they are needed and will build web service clients that will handle this. The cClientWebService class has been enhanced to know how to deal with these new nullable types and create appropriate requests and responses. The interface for all of this is private.
When working with simple types, this means you are using a struct instead of a simple type. This requires a little more code to use, but its usage is clear and straightforward. Most of the extra work is handled by the Studio and the cClientWebService class. The Studio will not use nullable datatypes if they are not needed.
This nullable support, which is now enabled by default, can be disabled for backwards compatibility and testing.
Better Nillable Support (Uncheck for Legacy Behavior)
XML types can also be defined as nillable, which is yet another way of handling null values. In this case, the schema marks a type or element with a nillable="true" attribute, usually setting minOccurs="1". This means that when the value is null, the element must be sent but should be sent with a special nill attribute (nill="true"). This is also supported as part of these changes. If a DataFlex nullable type is set to null (i.e., you set the bNull member to true), it will pass a nillable node with a nill="true" attribute. It will handle a nill="true" response by setting the bNull element to true.
The nillable support can also be enabled or disabled for backwards compatibility and testing.
From a developer's point of view, all of this is built in. If your web service uses nulls and you create the class with the new null type support, you now have a way of marking a type as null.
Both of these changes required Studio and cClientWebService class changes.
Use Better Struct Naming Resulting in Easier to Read Names (Uncheck for Legacy Behavior)
Complex web services often ended up with some strange looking names, with odd numbers and strange suffixes in their names. This was done to make sure everything generated by the web service class generator had unique names. The entire process for creating these names has been changed and the naming is dramatically improved. You will only see strangely "decorated" names when they are actually required to avoid duplicate names.
An example of this improvement would be:
Enum_List
Define C_tWSChoice_74_Locale
Define C_tWSChoice_74_ZipCode
End_Enum_List
Struct tWSChoice_74
Integer eChoice
String Locale
String ZipCode
End_Struct
Struct tWSFinderCode
tWSChoice_74 Choice_74
String Description
End_Struct
Now generates the following:
Enum_List
Define C_tWSFinderCode_Choice_Locale
Define C_tWSFinderCode_Choice_ZipCode
End_Enum_List
Struct tWSFinderCode_Choice
Integer eChoice
String Locale
String ZipCode
End_Struct
Struct tWSFinderCode
tWSFinderCode_Choice Choice
String Description
End_Struct
This makes the code easier to read and easier to work with. If your service contains names with strange suffixes or odd embedded numbers, you may wish to try regenerating these services with the new naming system.
This change is optional and can be disabled for compatibility purposes.
Force Requests to Bare Format / Force Responses to Bare Format
Most web services are document type services. In theory, this means that the entire request or the entire response can be treated as a single piece of data - a document. This was meant to be different than RPC type services (remote procedure call) where the data must be defined like a normal computer language function call with separate in and out parameters.
The catch is that document style web services can also be used to model RPC style services. In fact, modeling a document type service in an RPC style is the most common way to use services. RPC style better models the web service's use as a function call. Restated, most modern SOAP services are document type and not RPC type (these are formally defined SOAP types). However, that document can be and usually is treated in an RPC style.
For example, a web service that adds two numbers and returns the sum might be defined on the server as follows:
// Adds two numbers and returns the result.
Function AddNumber Real Number1 Real Number2 returns Real
This is normally modeled on the client in a very straightforward manner:
// Adds two numbers and returns the result.
Function wsAddNumber Real llnumber1 Real llnumber2 returns Real
Even though this is a document style SOAP service, we model it as a function call that closely matches the way it is defined on the server.
This could also be modeled in a pure document style where the entire request and/or the entire response is treated as a single piece of data. When that happens, this is referred to as being a bare request and/or bare response.
Struct tWSAddNumber
Real number1
Real number2
End_Struct
Struct tWSAddNumberResponse
Real AddNumberResult
End_Struct
// Adds two numbers and returns the result - bare style
Function wsAddNumber tWSAddNumber llAddNumber returns tWSAddNumberResponse
With either method, the XML data sent and received is identical. However, the mechanism you use to work with the data changes. In the above example, the regular non-bare style is much easier to work with.
As an aside, the term bare is used because the entire XML node inside of the section is treated as a single piece of "bare" data. Regular style goes in one more level and pulls the parameters out of the document for you.
In some cases, a web service cannot be properly represented as a procedure or function call with multiple parameters. When that happens, the service must be called using the bare style. Bare style is therefore the most flexible because any web service can be treated as bare. However, it is often not the best way to represent the service.
Using a service in a bare or regular style requires changes in how the client web class is generated. It is the job of the Studio's client web service generator to detect if a service's request and its response is best handled bare or regular. The Studio has always done this and it does this for each method in a web service. In each method, it marks a request as bare by setting [pbRequestBare](../VdfClassRef/Windows/cClientWebService-Property-pbRequestBare.md) to True. It marks a response as bare by setting [pbResponseBare](../VdfClassRef/Windows/cClientWebService-Property-pbResponseBare.md) to True.
All web services can be represented bare style, but not all web services can be represented regular style. In some cases, it is impossible to represent a service regular style and the Studio will detect this. In other cases, it is possible to represent the service regular but it is not the best choice as the service was actually designed to be bare. While using it regular style will work, it now makes the service appear more complicated. When this happens, the Studio's analysis of the service did not arrive at the best choice. In those cases, you will want to work with a service bare style even though it could be used regular style. To handle this, we now have an option in the client web service generator where you can force the web service's request and/or its response to be bare. If you think a service would be better handled bare, you can try generating the service forcing the request or response to be bare.
It is unlikely this will ever need this option. This can be useful for debugging and it can be useful in better understanding how web services work.