3

Data Types in WSDL

 

In this chapter, we shall explore different data types in greater depth and shall analyze the transformation that occurs when data is sent across from client to server and vice-versa. Be warned! While probing for answers, the journey may not be all that smooth. For the sake of conceptual clarity and since we feel that it is our moral responsibility to do so, we shall explicate the wsdl process yet again. However, we hope to be very succinct in our explanation.

 

As usual, we start with the file a.asmx. This file has to be placed in the c:\inetpub\wwwroot subdirectory.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(ref int i)

{

i = 10;

}

}

 

 

The function abc in the file accepts only one parameter named i, of data type int, which is passed by reference. For those of you who have been initiated only recently to the world of C#, a ref parameter needs to be initialized before it is passed as a parameter. If the function changes the value contained in the parameter, the change is reflected back in the main program also. The abc function alters the value of i to 10.

 

Run Internet Explorer and enter the following URL: http://localhost/a.asmx?WSDL. This will generate and display a wsdl file.

 

If we display the entire WSDL file, this book is likely to weigh a ton. Therefore, we have sifted through and filtered out the code that is superfluous and dispensable, and saved the contents in file aa.wsdl in a newly created folder named www. This file is displayed below for your reference.

 

aa.wsdl

<?xml version="1.0" encoding="utf-8" ?>

<definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s0="http://tempuri.org/" targetNamespace="http://tempuri.org/" xmlns="http://schemas.xmlsoap.org/wsdl/">

<types>

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

<s:element name="abc">

<s:complexType>

<s:sequence>

  <s:element minOccurs="1" maxOccurs="1" name="i" type="s:int" />

  </s:sequence>

  </s:complexType>

  </s:element>

<s:element name="abcResponse">

<s:complexType>

<s:sequence>

  <s:element minOccurs="1" maxOccurs="1" name="i" type="s:int" />

  </s:sequence>

  </s:complexType>

  </s:element>

  </s:schema>

  </types>

<message name="abcSoapIn">

  <part name="parameters" element="s0:abc" />

  </message>

<message name="abcSoapOut">

  <part name="parameters" element="s0:abcResponse" />

  </message>

<portType name="zzzSoap">

<operation name="abc">

  <input message="s0:abcSoapIn" />

  <output message="s0:abcSoapOut" />

  </operation>

  </portType>

<binding name="zzzSoap" type="s0:zzzSoap">

  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />

<operation name="abc">

  <soap:operation soapAction="http://tempuri.org/abc" style="document" />

<input>

  <soap:body use="literal" />

  </input>

<output>

  <soap:body use="literal" />

  </output>

  </operation>

  </binding>

<service name="zzz">

<port name="zzzSoap" binding="s0:zzzSoap">

  <soap:address location="http://localhost:8080/a.asmx" />

  </port>

  </service>

  </definitions>

 

A major portion of the wsdl file remains unchanged. There however are a few modifications in the type element, i.e. in the schema. The element of abc is created like earlier, and the parameter i becomes the child element.

 

The parameter i is read/write, and is sent across as the ref parameter. It is mandatory for the client that calls the code in the server, to specify a value. Thus, the SOAP request packet displays the value of this parameter.

 

The second element in the types is the abcResponse. This element too has a child element called i, which is exactly similar to the first i child. This is for the simple reason that the function abc in the asmx file, is permitted to assign a value and return it back. Thus, all ref parameters are sent to and fro in the SOAP payload.

 

We have effected a modification to the Uri property. To be precise, we have altered the location attribute in soap:address element, within the service tag. The port number of 8080 is specified, in place of 80. This is because, we intend to run the trace utility from the SOAP Toolkit, in order to view the packets flowing to and fro. In the www folder, write the program given below in a file called a.cs.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

int b = 100;

a.abc(ref b);

System.Console.WriteLine(b);

}

}

 

The program a.cs is a client program in which, an int variable named b is initialized to 100, and passed as a parameter to the abc function. After the function concludes execution, the value of b is printed.

 

Prior to executing any program, start the trace utility from the SOAP toolkit. Then, create a batch file to hold a series of commands. Run the batch file and view the output in both, the console box as well as the trace window.

 

z.bat

del *.exe

del *.dll

del zzz.cs

wsdl aa.wsdl

csc /t:library zzz.cs

csc a.cs /r:zzz.dll

a

 

Output

10

 

Trace Output

Soap request

<soap:Body>

<abc xmlns="http://tempuri.org/">

<i>100</i>

</abc>

</soap:Body>

 

Soap Response

<soap:Body>

<abcResponse xmlns="http://tempuri.org/">

<i>10</i>

</abcResponse>

</soap:Body>

 

Since majority of the commands need to be reiterated, we create a batch file named z.bat to contain them. In this file, we initially delete all the output files that have been created earlier, and then, we call WSDL to create a proxy in the file zzz.cs.

 

The only parameter furnished to the WSDL program is the file aa.wsdl.  We hereby restate that the wsdl file is created by the web server, after it has read the asmx file. The proxy zzz.cs is then compiled to a dll, so that it may be used as a reference while creating the client executable named a.exe.

 

On running the client program 'a', the trace program exhibits the value of 100 in SOAP Request, and the value of 10 in the SOAP Response. i is set to 10 in the asmx file.

In the SOAP Request, the value of b i.e. 100, is sent across as the content of i. In the response packet, the value of 10 is sent across as the content of element i. There is no return value, because the abc function in the asmx file returns void.

 

Finally, in the C# code generated by the WSDL program, the abc function accepts a single ref parameter named i. The name of the parameter remains the same like the one in asmx file.

 

In zzz.cs

public void abc(ref int i)

{

object[] results = this.Invoke("abc", new object[] {i});

i = ((int)(results[0]));

}

 

The invoke function is called, with the second parameter as an array of one int. The return value stored in the array results[0], is type cast to an int and assigned to the ref parameter i. Previously, this value had been returned back. The entire exercise provides ample evidence of the fact that a ref parameter is visible in both, the response and request packets.

 

Let us now unveil the mechanism by means of which, an array is transmitted from a client to the server. The abc function in the asmx file accepts an array of ints in the variable i, which is the sole parameter.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(int [] i)

{

}

}

 

In aa.wsdl

<?xml version="1.0" encoding="utf-8" ?>

<definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s0="http://tempuri.org/" xmlns:s1="http://tempuri.org/AbstractTypes" targetNamespace="http://tempuri.org/" xmlns="http://schemas.xmlsoap.org/wsdl/">

<types>

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

<s:element name="abc">

<s:complexType>

<s:sequence>

  <s:element minOccurs="1" maxOccurs="1" name="i" nillable="true" type="s0:ArrayOfInt" />

  </s:sequence>

  </s:complexType>

  </s:element>

<s:complexType name="ArrayOfInt">

<s:sequence>

  <s:element minOccurs="0" maxOccurs="unbounded" name="int" type="s:int" />

  </s:sequence>

  </s:complexType>

<s:element name="abcResponse">    <s:complexType />    </s:element>

  </s:schema>

<s:schema targetNamespace="http://tempuri.org/AbstractTypes">

<s:complexType name="StringArray">

<s:complexContent mixed="false">

<s:restriction base="soapenc:Array">

<s:sequence>

  <s:element minOccurs="0" maxOccurs="unbounded" name="String" type="s:string" />

  </s:sequence>

  </s:restriction>

  </s:complexContent>

  </s:complexType>

  </s:schema>

  </types>

<message name="abcSoapIn">   <part name="parameters" element="s0:abc" />   </message>

<message name="abcSoapOut">   <part name="parameters" element="s0:abcResponse" />   </message>

<portType name="zzzSoap">

<operation name="abc">  

<input message="s0:abcSoapIn" />   <output message="s0:abcSoapOut" /> </operation>

  </portType>

<binding name="zzzSoap" type="s0:zzzSoap">

  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />

<operation name="abc">   <soap:operation soapAction="http://tempuri.org/abc" style="document" />

<input>   <soap:body use="literal" />    </input>

<output>   <soap:body use="literal" />    </output>

  </operation>

  </binding>

<service name="zzz"> <port name="zzzSoap" binding="s0:zzzSoap">

  <soap:address location="http://localhost:8080/a.asmx" />

  </port>   </service>

  </definitions>

 

Henceforth, in order to keep this book trim, we shall exhibit only the variations in the file, instead of the entire file, unless required otherwise. We normally delete the element block containing the words HttpGet and HttpPost from the wsdl file. This block is created by the server, while the asmx file is being loaded in the browser window.

 

In the wsdl file, the abc element reveals a complexType with a sequence. This is the same as before. The 'nillable' attribute determines whether a value of 'null' is permissible or not.

 

<s:element name="abc">

<s:complexType>

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" nillable="true" type="s0:ArrayOfInt" />

</s:sequence>

</s:complexType>

</s:element>

However, the array element i, now has the type attribute initialized to an ArrayOfInt. This type does not originate from the XML Schema namespace, since XML Schemas do not contain any arrays. But in future, if the XML world decides to support arrays, the WSDL world is sure to voluntarily take away its support for arrays. The next element definition creates complexType with a name of ArrayOfInt.

 

<s:complexType name="ArrayOfInt">

<s:sequence>

<s:element minOccurs="0" maxOccurs="unbounded" name="int" type="s:int" />

</s:sequence>

</s:complexType>

 

Here, unlike in the earlier case, the complexType element has been assigned a name, thereby, facilitating its reuse in the file.  The element within the sequence of complexType has been named int. The minOccurs has a value of zero, which is indicative of the fact that the element is optional. The maxOccurs has a value of 'unbounded', allowing an indefinite count of int child elements. Finally, the type is specified as an int, which is one of the predefined data types of the XML Schema.

 

<s:schema targetNamespace="http://tempuri.org/AbstractTypes">

<s:complexType name="StringArray">

<s:complexContent mixed="false">

<s:restriction base="soapenc:Array">

<s:sequence>

<s:element minOccurs="0" maxOccurs="unbounded" name="String" type="s:string" />

</s:sequence>

</s:restriction>

</s:complexContent>

</s:complexType>

</s:schema>

 

There is one more schema root element, which belongs to the targetNamespace. The name of the complexType is StringArray, and it commences with a complexContent element. The responsibility of this element is to interpret mixed content.

 

The mixed attribute has a default value of 'false'. The value assigned to this attribute resolves whether character data is permitted between child elements or not.

 

The 'restriction' element permits the use of an existing type, but it confines the range values that it can assume. Here, the base type that is to be restricted, is the Array type from the soapenc namespace.

 

It is pertinent to note that the soapenc namespace deals with types that are not embodied in the XML Schema definition. The restriction element contains an element called String, having the datatype of string, which can occur innumerable times. This complexType has not been used anywhere in our program.

 

<s:element name="abcResponse">

<s:complexType />

</s:element>

 

The return value for abc in the asmx file is void. Therefore, the element named abcResponse has a complexType, which contains no elements whatsoever.

 

In the client file a.cs, an array variable b of type int is created, and thereafter, initialized to the three values of 1, 3 and 5. The array is then supplied as a parameter to the function abc.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

int []b ;

b = new int[3]{1,3,5};

a.abc(b);

}

}

 

Run the batch file named z.bat, and observe the output in the trace window.

 

SOAP request

<abc xmlns="http://tempuri.org/">

<i>

<int>1</int>

<int>3</int>

<int>5</int>

</i>

</abc>

 

SOAP response

<soap:Body>

<abcResponse xmlns="http://tempuri.org/" />

</soap:Body>

 

In the SOAP request, the child element is named as i, followed by the familiar element int, as is observed in the WSDL file. This acquaints us with the fact that the data type of the array is an int. This name is not binding. In fact, any other name could have been used in its place. It is the name of the parameter i that is of prime significance.

 

The SOAP response is empty, and in the time to come, we shall bore you stiff, by making a display of it. The file zzz.cs or proxy makes for an interesting reading.

 

In zzz.cs

public void abc(

[System.Xml.Serialization.XmlArrayItemAttribute ("int", IsNullable=false) ] int[] i)

{

this.Invoke("abc", new object[] {i});

}

 

The parameter i is tagged with an attribute named XmlArrayItemAttribute, which specifies the name as int. Therefore, we see the word 'int' in the SOAP packet.

 

The property IsNullable establishes whether the array should be serialized in case it is a null or otherwise. In this case, since the IsNullable property is initialized to 'false', only the values shall be sent across. We shall anatomize and analyze this concept later.

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(string [] i , int [] j)

{

}

}

 

The asmx file now contains the same abc function, but the function now has two parameters, i and j, which are arrays of type string and int, respectively.

 

aa.wsdl

<types>

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

<s:element name="abc">

<s:complexType>

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" nillable="true" type="s0:ArrayOfString" />

<s:element minOccurs="1" maxOccurs="1" name="j" nillable="true" type="s0:ArrayOfInt" />

</s:sequence>

</s:complexType>

</s:element>

<s:complexType name="ArrayOfString">

<s:sequence>

<s:element minOccurs="0" maxOccurs="unbounded" name="string" nillable="true" type="s:string" />

</s:sequence>

</s:complexType>

<s:complexType name="ArrayOfInt">

<s:sequence>

<s:element minOccurs="0" maxOccurs="unbounded" name="int" type="s:int" />

</s:sequence>

</s:complexType>

<s:element name="abcResponse">

<s:complexType />

</s:element>

</s:schema>

</types>

 

In the truncated aa.wsdl file, we first observe the element abc with a sequence containing two children i and j, of type ArrayOfString and ArrayOfInt, respectively. Both the types belong to the namespace s0. The sequence of the elements is important. Therefore, the elements i and j are in a predetermined order.

 

Then comes the complexType definition for the ArrayOfString. It is analogous to the ArrayOfInt definition. The only distinction lies in the type attribute, which refers to a string from the XML Schema namespace, and not to an int. The string and int are predefined data types.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

string[]b ;

int []c ;

b = new string[3]{"Hi","bye","No"};

c = new int[2]{10,20};

a.abc(b,c);

}

}

 

The client program a.cs has two arrays b and c, declared to be of type string and int, respectively. They are originally created with 3 and 2 members, respectively. They are then initialized to some values; and thereafter, passed to the abc function.

 

SOAP request

<soap:Body>

<abc xmlns="http://tempuri.org/">

<i>

<string>Hi</string>

<string>bye</string>

<string>No</string>

</i>

<j>

<int>10</int>

<int>20</int>

</j>

</abc>

 

The SOAP request in the trace output shows the two child elements in the correct sequence, i.e. i followed by j.

 

The three string members are securely ensconced within the string element, and the int child contains the second array.

 

In zzz.cs

public void abc([System.Xml.Serialization.XmlArrayItemAttribute("string", IsNullable=true)] string[] i, [System.Xml.Serialization.XmlArrayItemAttribute("int", IsNullable=false)] int[] j)

{

this.Invoke("abc", new object[] {i, j});

}

 

The zzz.cs file also contains two array parameters with the same attribute as was discussed in the earlier program. However, each attribute is allotted a distinct name. There is hardly any difference between an array of strings and an array of ints.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(int [,] i)

{

}

}

Error in Browser

System.Exception: Method zzz.abc can not be reflected. ---> System.Exception: Multi-dimensional arrays are not supported. Use a jagged array instead.

 

A multi dimensional array when used in the asmx file, generates an error; it is so because the concept of an array is not supported in WSDL. The best alternative that comes to mind is the utilization of a jagged array. The function abc has now been modified to accept values into a jagged array.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(int[][] i)

{

}

}

 

Let us first focus on the file a.cs, in which the concept of a jagged array has been explicated with considerable lucidity.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

int[][] b = new int[2][] { new int[] {1,2,3}, new int[] {4,5} };

a.abc(b);

}

}

 

In a.cs, first an array of size two is created. Then, the two arrays are initialized to arrays of ints; with one having three members, and the other having two members. The contents of the aa.wsdl file are indeed quite attention grabbing!

 

In aa.wsdl

<types>

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

<s:element name="abc">

<s:complexType>

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" nillable="true" type="s0:ArrayOfArrayOfInt" />

</s:sequence>

</s:complexType>

</s:element>

<s:complexType name="ArrayOfArrayOfInt">

<s:sequence>

<s:element minOccurs="0" maxOccurs="unbounded" name="ArrayOfInt" nillable="true" type="s0:ArrayOfInt" />

</s:sequence>

</s:complexType>

<s:complexType name="ArrayOfInt">

<s:sequence>

<s:element minOccurs="0" maxOccurs="unbounded" name="int" type="s:int" />

</s:sequence>

</s:complexType>

<s:element name="abcResponse">

<s:complexType />

</s:element>

</s:schema>

</types>

 

Only one element i, of type ArrayOfArrayOfInt, is visible in the file. A few lines later, the type is specified as a complexType. It has one member ArrayOfInt, whose type is also ArrayOfInt. Thereafter, the type ArrayOfInt is created to accept a single element int, of type int, from the XML Schema type.

 

The element i has minOccurs of 1 and maxOccurs of 1, thereby, making its presence obligatory. The other types have the values for the attributes minOccurs and maxOccurs assigned in such a manner, that they could contain values ranging from none to infinity!

 

SOAP request

<abc xmlns="http://tempuri.org/">

<i>

<ArrayOfInt>

<int>1</int>

<int>2</int>

<int>3</int>

</ArrayOfInt>

<ArrayOfInt>

<int>4</int>

<int>5</int>

</ArrayOfInt>

</i>

</abc>

 

The SOAP request launches with the parameter name i, followed by an element called ArrayOfInt, as specified by the WSDL file. There are two ArrayOfInt child elements, since the first array has two members. The first child has three int elements and the second child has two int elements. The number of elements is contingent upon the size specified while they are being created in the file. This is how the concept of a jagged array is implemented in SOAP and WSDL.

 

zzz.cs

public void abc([System.Xml.Serialization. XmlArrayItemAttribute("ArrayOfInt", IsNullable=true)] [System.Xml.Serialization.XmlArrayItemAttribute("int", IsNullable=false, NestingLevel=1)] int[][] i)

{

this.Invoke("abc", new object[] { i});

}

 

The zzz.cs file has two attributes as there are two arrays. The first is an ArrayOfInt, while the second is an int. The NestingLevel indicates that it is one level below the main level. Earlier on, the attribute of XmlArrayItemAttribute had a single level.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(yyy a)

{

}

}

public class yyy

{

}

 

In this example, a simple object, which is an instance of class yyy, is passed as a parameter to the function abc. Both the classes yyy and zzz are in the same asmx file. But they could have jolly well been separated, in other words, the class could have been placed in a dll, in the bin folder. We have placed them in the same file for the sake of convenience. The class is absolutely empty, i.e. it has no properties or functions or variables or for that matter, anything at all!

 

The wsdl file provided by the web server has the element named abc with the complex type named as a, having the type yyy, from the s0 namespace. The yyy complexType is then defined as an empty type, since the class yyy does not contain any members.

 

aa.wsdl

<types>

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

<s:element name="abc">

<s:complexType>

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="a" nillable="true" type="s0:yyy" />

</s:sequence>

</s:complexType>

</s:element>

<s:complexType name="yyy" />

<s:element name="abcResponse">

<s:complexType />

</s:element>

</s:schema>

</types>

 

The client program simply creates a yyy object 'b', and calls the function abc with the object b as a parameter.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

yyy b = new yyy();

a.abc(b);

}

}

 

SOAP Request

<soap:Body>

<abc xmlns="http://tempuri.org/">

<a />

</abc>

</soap:Body>

 

The SOAP request displays the element a as the name of the parameter with empty content, since the class yyy has no members. Also, the proxy generated contains the function abc with the yyy object as a parameter. The Invoke function creates an array of objects, with a single member named 'a'.

 

zzz.cs

public void abc(yyy a)

{

this.Invoke("abc", new object[] {a});

}

public class yyy

{

}

 

The next program demonstrates passing objects or reference types.

Here, there is a class yyy containing two int variables, i and j. The function abc returns the sum of these two int variables.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public int abc(yyy a)

{

return a.i + a.j;

}

}

public class yyy

{

public int i,j;

}

 

The wsdl file, like earlier, contains the same parameter 'a' as an element. But the complexType definition for yyy has undergone transformation. Earlier, it was an empty type, but now it contains the variables i and j, as elements in a sequence. It also has attributes, which mandate their occurrence, once and only once.

 

aa.wsdl

<types>

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

<s:element name="abc">

<s:complexType>

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="a" nillable="true" type="s0:yyy" />

</s:sequence>

</s:complexType>

</s:element>

<s:complexType name="yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" type="s:int" />

<s:element minOccurs="1" maxOccurs="1" name="j" type="s:int" />

</s:sequence>

</s:complexType>

<s:element name="abcResponse">

<s:complexType>

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="abcResult" type="s:int" />

</s:sequence>

</s:complexType>

</s:element>

</s:schema>

</types>

 

The client program creates a yyy object 'b', and then, initializes the i and j members to 10 and 20, respectively. Thereafter, it calls the function abc with the object 'b' as a parameter. The return value, which obviously is 30, is stored in a variable named 'p' and thereafter, it is displayed.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

yyy b = new yyy();

b.i = 10;

b.j=20;

int p = a.abc(b);

System.Console.WriteLine(p);

}

}

 

Output

30

 

The SOAP payload very clearly illustrates how an object is sent across in a SOAP request.

 

SOAP request

<abc xmlns="http://tempuri.org/">

<a>

<i>10</i>

<j>20</j>

</a>

</abc>

 

SOAP response

<abcResponse xmlns="http://tempuri.org/">

<abcResult>30</abcResult>

</abcResponse>

 

Both the public members are serialized on the wire. This implies that the object 'b' is not sent across. Instead, the values of the individual variables are dispatched. Thus, the element 'a' carries the values 10 and 20, enveloped within elements i and j, respectively.

 

This highlights the fact that objects are passed by value, and not by reference. The return packet graciously carries the value of 30 in the abcResult element.

 

zzz.cs

public int abc(yyy a)

{

object[] results = this.Invoke("abc", new object[] {a});

return ((int)(results[0]));

}

public class yyy

{

public int i;

public int j;

}

 

The proxy merely passes the yyy object 'a' to the Invoke function, and type casts the return value to an int. The only variation incorporated here is that, the class yyy has two public variables, i and j.

 

In this slightly larger asmx file, the class yyy contains a public variable i, a private variable j, and a protected variable k. Further, three public properties i1, i2 and i3 are also present.

 

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public int abc(yyy a)

{

a.i = 1000;

return a.i2;

}

}

public class yyy

{

public int i;

private int j;

protected int k;

public int i1

{

get

{

return 11;

}

set

{

}

}

public int i2

{

get

{

return 12;

}

}

public int i3

{

set

{

}

}

}

 

Since the variables j and k are not marked as public in the proxy named zzz.cs, the class yyy displays only the variable i. Thus, only public members are serialized, whereas, protected and private members are not.

 

zzz.cs

public class yyy

{

public int i;

public int i1;

}

 

Furthermore, only the property of i1 is visible, since it is in possession of both, a get and a set accessor. The properties of i2 and i3 have only one accessor. Therefore, they are not taken into consideration. In order to be serialized, properties have to be both, public and read/write.

 

The wsdl file depicts the element 'a' as before, with the type yyy. The complex type yyy now holds two elements named i and i1, in a specific sequence.

 

aa.wsdl

<types>

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

<s:element name="abc">

<s:complexType>

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="a" nillable="true" type="s0:yyy" />

</s:sequence>

</s:complexType>

</s:element>

<s:complexType name="yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" type="s:int" />

<s:element minOccurs="1" maxOccurs="1" name="i1" type="s:int" />

</s:sequence>

</s:complexType>

<s:element name="abcResponse">

<s:complexType>

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="abcResult" type="s:int" />

</s:sequence>

</s:complexType>

</s:element>

</s:schema>

</types>

 

The rules that apply to a proxy are binding on the wsdl file too. Only public members and properties containing both, the accessors of get and set, are visible. Technically, there is no concept of a 'property' in a wsdl file. Thus, in the context of a wsdl file, there is no clear-cut distinction between properties and variables.

 

The abcResponse element is not affected by the introduction of the new instance variables and properties.

 

The client can only initialize the members of i and i1. The other two variables, j and k cannot be accessed, since they remain out of bounds or off-limits. The property i3 has a set accessor and therefore, this too cannot be accessed. Besides, there is no indication of a variable by that name in the class yyy, within the zzz.cs file.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

yyy b = new yyy();

b.i = 450;

b.i1 = 45;

int p = a.abc(b);

System.Console.WriteLine(p);

System.Console.WriteLine(b.i);

}

}

 

Output

12

450

 

SOAP request

<abc xmlns="http://tempuri.org/">

  <a>

  <i>450</i>

  <i1>45</i1>

  </a>

  </abc>

 

SOAP response

<abcResponse xmlns="http://tempuri.org/">

  <abcResult>12</abcResult>

  </abcResponse>

 

The SOAP request is straightforward, wherein, the values of 450 and 45 are ensconced in the elements of i and i1. The names of the elements are the same as in the class yyy, within the a.asmx file.

 

The return value that too is sent across is 12. This is the value that the property i2 returns. Thus, we are permitted to return the value of a property, since properties behave like variables, but they contain barrels full of code. The code for the property dwells on the server. Thus, the client need not lose sleep over its execution.

 

The server modifies the value of the variable i to 1000. However, this change is not reflected in any way in the SOAP response that is dispatched across.

 

Objects are sent across as values. Thus, there is no mechanism, by means of which, this change that is effected by the server, can be passed onto the client. The return statement explicitly has the property of i2. Hence, the value that is displayed on the screen and is transported across, is 12.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(yyy a)

{

}

}

public class yyy

{

public yyy(int i)

{

}

}

 

Error

yyy cannot be serialized because it does not have a default public constructor.

 

Exception Details: System.Exception: yyy cannot be serialized because it does not have a default public constructor.

 

The above asmx file throws an exception. This is because the class yyy now has a constructor with an int parameter. With the introduction of this, the free constructor that has no parameters, gets swept off its feet. Every object that gets serialized, must have a public constructor taking no parameters.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(yyy a)

{

}

}

public class yyy

{

public yyy(int i)

{

}

public yyy()

{

}

}

 

zzz.cs

public class yyy

{

}

 

When we add the constructor having no parameters to the asmx file, the error vanishes.  However, the proxy that is generated, ignores the constructors completely, and generates an empty class altogether.

 

This demonstrates the fact that a constructor which has no parameters, is worthless, since the proxy class would not reflect such a constructor in the class. This is for the reason that the WSDL language is devoid of adequate intelligence, to be able to handle functions.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(yyy a)

{

}

}

public class yyy

{

public int this[string a]

{

get

{

return 100;

}

set

{

}

}

}

zzz.cs

public class yyy

{

}

 

The concept of an indexer is also not passed on to the WSDL file. Hence, there is no sign of it in the proxy also. Thus, despite the presence of a valid indexer in the class yyy of public access with a get and set accessor, it is not reflected in the proxy file zzz.cs. This substantiates the fact that indexers are incapable of being activated remotely.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz {

[WebMethod]

public void abc(yyy a)

{

}

}

public class yyy

{

public static int i;

public readonly int k;

}

 

zzz.cs

public class yyy

{

public int k;

}

 

The static variable i does not exist in the class yyy. Only the 'read only' variable k is present in the class, but it is without the 'read only' attribute. However, we are not allowed to change the value of the variable k in the function abc, since the class yyy has been informed about the 'read only' attribute in the asmx file.

 

The main aim of analyzing the above programs is to demonstrate that, not all data types can be used in the asmx file.

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(yyy a)

{

}

}

public class yyy

{

public int i;

}

public class xxx : yyy

{

public int j;

}

 

aa.wsdl

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

  <s:element name="abc">

<s:complexType>

  <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="a" nillable="true" type="s0:yyy" />

  </s:sequence>

</s:complexType>

  </s:element>

  <s:complexType name="yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" type="s:int" />

</s:sequence>

  </s:complexType>

  <s:element name="abcResponse">

<s:complexType />

  </s:element>

</s:schema>

 

 

zzz.cs

public class yyy

{

public int i;

}

 

The asmx file has the abc function, which accepts a yyy object. The class yyy has been created to contain a single variable i of datatype int. Also, another class named xxx is defined in the file, which derives from class yyy. The xxx class contains an int variable named j. So far so good!

 

The aa.wsdl file, as always, contains the initial set of statements, along with the parameter 'a' of type yyy. The complex type named yyy has a single element, which is an int named i.  There is no trace of the class xxx in the entire file. As the proxy is created from the contents of the wsdl file, the zzz.cs file too has no reference to the class xxx. It only contains information about the members of class yyy. These are the perils of working with beta software!

 

In the next program, the only change that has been incorporated is, the addition of a function called pqr, in the existing asmx file. It accepts an instance of class xxx as a parameter.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(yyy a)

{

}

[WebMethod]

public void pqr(xxx b)

{

}

}

public class yyy

{

public int i;

}

public class xxx : yyy

{

public int j;

}

 

The wsdl file now contains many new statements as a outcome of the introduction of the function pqr.

 

aa.wsdl

<types>

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

<s:element name="abc">

<s:complexType>

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="a" nillable="true" type="s0:yyy" />

</s:sequence>

</s:complexType>

</s:element>

<s:complexType name="yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" type="s:int" />

</s:sequence>

  </s:complexType>

  <s:element name="abcResponse">

<s:complexType />

  </s:element>

  <s:element name="pqr">

<s:complexType>

  <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="b" nillable="true" type="s0:xxx" />

  </s:sequence>

</s:complexType>

  </s:element>

  <s:complexType name="xxx">

<s:complexContent mixed="false">

  <s:extension base="s0:yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="j" type="s:int" />

</s:sequence>

  </s:extension>

</s:complexContent>

  </s:complexType>

  <s:element name="pqrResponse">

<s:complexType />

  </s:element>

</s:schema>

</types>

 

As before, the element abc has a single parameter 'a', of type yyy. The complexType for the yyy type, has an element i of type int.

 

Next, we encounter the empty abcResponse element. Every function with a WebMethod attribute is represented by an element in the wsdl world. Therefore, the element pqr is visible in the file. This element has an element b of type xxx.

 

Instead of the regular sequence, the complex type xxx has a complexContent element. A new element by the name of 'extension' is located within the complexContent element. Unlike 'restriction' that restricts values, the element 'extension' simply extends the base attribute, i.e. a class. Thus, the xxx complexType extends the complexType class. The symbol of colon, used for derivation in C#, is neatly replaced by the word 'extension'. With this extension element, all the members of the class xxx can be specified. Presently, we have only one member called j, of type int. The pqrResponse element requires no explanation, since it is empty.

 

In the client program, we do things a little differently. Instead of creating a yyy object, we create an xxx object.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

xxx c = new xxx();

c.i = 10;

c.j = 20;

a.abc(c);

}

}

 

The two members of class xxx, viz. i from yyy and j from class xxx, are initialized after creating an instance of class xxx. The function abc is passed an xxx object, and not a yyy object, as specified in the function parameter list. This does not bring about any error because, wherever a base class is specified, a derived class can be used interchangeably.

 

The question that crops up at this stage is that- On receiving the SOAP packet, how is the asmx file able to establish that the original object is of type xxx and not of yyy?

 

The SOAP request utilizes a feature of the XML schema world, wherein it seeks out an attribute called type, in the xsi namespace. This attribute specifies the run time data type of the object.

 

SOAP request

<abc xmlns="http://tempuri.org/">

<a xsi:type="xxx">

  <i>10</i>

  <j>20</j>

</a>

</abc>

 

Thus, the proxy class reveals both the classes of yyy and xxx, and the fact that the xxx class is derived from yyy.

 

zzz.cs

[System.Xml.Serialization.XmlIncludeAttribute (typeof(xxx))]

public class yyy

{

public int i;

}

public class xxx : yyy

{

public int j;

}

There is also an extra attribute called XmlIncludeAttribute, together with class yyy, which is indicative of the fact that the yyy class is being used as a base class for the xxx class.

 

Thus, when the server receives this packet, it is aware that the parameter 'a' has two members i and j, and that it has received an xxx type and not the original yyy type. Despite both i and j being received by the server in the function abc, only i can be accessed and not j. An alternative approach would be to package only the members of the yyy class across the wire.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(yyy a , xxx b , yyy c)

{

}

[WebMethod]

public void pqr(www c)

{

}

}

public class yyy

{

public int i;

}

public class xxx : yyy

{

public int j;

}

public class www : xxx

{

public int k;

}

 

aa.wsdl

  <types>

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

  <s:element name="abc">

<s:complexType>

  <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="a" nillable="true" type="s0:yyy" />

<s:element minOccurs="1" maxOccurs="1" name="b" nillable="true" type="s0:xxx" />

<s:element minOccurs="1" maxOccurs="1" name="c" nillable="true" type="s0:yyy" />

  </s:sequence>

</s:complexType>

  </s:element>

  <s:complexType name="yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" type="s:int" />

</s:sequence>

  </s:complexType>

  <s:complexType name="xxx">

<s:complexContent mixed="false">

  <s:extension base="s0:yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="j" type="s:int" />

</s:sequence>

  </s:extension>

</s:complexContent>

  </s:complexType>

  <s:element name="abcResponse">

<s:complexType />

  </s:element>

  <s:element name="pqr">

<s:complexType>

  <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="c" nillable="true" type="s0:www" />

  </s:sequence>

</s:complexType>

  </s:element>

  <s:complexType name="www">

<s:complexContent mixed="false">

  <s:extension base="s0:xxx">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="k" type="s:int" />

</s:sequence>

  </s:extension>

</s:complexContent>

  </s:complexType>

  <s:element name="pqrResponse">

<s:complexType />

  </s:element>

</s:schema>

  </types>

 

In this program, one more class named www, derived from class xxx and which contains a single variable k, has been added. The abc function takes three parameters:

     The first parameter is a yyy type.

     The second is an xxx.

     The third is yet again a yyy object.

 

The wsdl file is a mirror image of the asmx file. Thus, there are three child elements in the abc element, viz. a, b and c, of type yyy, xxx and yyy, respectively.

 

The complexType yyy, as is usually the case, has a sequence with one element i of type int. The complexType xxx has a complexContent, with an extension element that extends the type yyy. There exists the newly added element j, similar to what was observed in the earlier program. The newly introduced type is the www complexType. It has been extended from the type xxx, and the sequence has an element k. Thus, all extensions behave in a similar fashion.

 

In the client program a.cs, an object d of type www is created, and the three members i, j and k of the object, are initialized to 100,200 and 300, correspondingly. This object d is passed twice as a parameter to the function abc.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

www d = new www();

xxx e = new xxx();

d.i=100;

d.j=200;

d.k=300;

e.i=10;

e.j=20;

a.abc(d,d,e);

}

}

 

One more object e, which is an instance of class xxx, is created and passed as the last parameter to the function abc. This is accomplished after initializing the two members i and j, to the values of 10 and 20, respectively. Bear in mind that the function requires parameters in the sequence of yyy, xxx and yyy. There is no mention of a www object.

 

Now, we take a look at the SOAP request, where the function name abc is reflected as the main element.

 

SOAP request

<abc xmlns="http://tempuri.org/">

- <a xsi:type="www">

  <i>100</i>

  <j>200</j>

  <k>300</k>

  </a>

- <b xsi:type="www">

  <i>100</i>

  <j>200</j>

  <k>300</k>

  </b>

- <c xsi:type="xxx">

  <i>10</i>

  <j>20</j>

  </c>

  </abc>

 

The packet displays the first parameter 'a', which comprises of the values of 100, 200 and 300. However, in this case, the type attribute is initialized to the run-time data type of a www object, and not of a yyy object.

 

The second parameter 'b' is a clone of 'a'. Here too, the run-time data type is www, in place of xxx.  The third parameter 'c' is expected to be a yyy data type. However, we have provided an xxx data type. Hence, the type attribute shows the type as xxx. The Invoke function in the proxy, does not show any variation at all.

 

zzz.cs

public void abc(yyy a, xxx b, yyy c)

{

this.Invoke("abc", new object[] {a, b, c});

}

 

[System.Xml.Serialization.XmlIncludeAttribute(typeof(xxx))]

[System.Xml.Serialization.XmlIncludeAttribute(typeof(www))]

public class yyy

{

public int i;

}

[System.Xml.Serialization.XmlIncludeAttribute(typeof(www))]

public class xxx : yyy

{

public int j;

}

public class www : xxx

{

public int k;

}

 

The class yyy is now used as a base class for the two classes, xxx and www. For the very same reason, the 'include' attribute is repeated twice. The class www derives only from xxx. Hence, the 'include' attribute is used only once.

 

In the next round, we deter from making any amendment to the asmx file. Therefore, the wsdl file and the proxy, remain the same.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

yyy c = new xxx();

xxx d = new www();

yyy e = new www();

c.i = 1;

d.i = 20;

e.i = 30;

a.abc(c,d,e);

}

}

 

SOAP request

<a xsi:type="xxx">

  <i>1</i>

  <j>0</j>

  </a>

- <b xsi:type="www">

  <i>20</i>

  <j>0</j>

  <k>0</k>

  </b>

- <c xsi:type="www">

  <i>30</i>

  <j>0</j>

  <k>0</k>

  </c>

</abc>

 

The client program creates a yyy object c, but initializes it to an xxx object. Thus, its run time data type is xxx, and not yyy. It has only one member viz. i, that is initialized to 1. As the data type of c is yyy, initializing the j member will result in a compiler error.

 

Thereafter, c is passed as the first parameter to the function. The SOAP request displays the type attribute having a value of xxx, and the element j with the content as 0. In an object, un-initialized variables are always assigned a value of zero.

The second parameter d is an xxx object, which is initialized to a higher object named www. Both the variables of i and j could have been initialized, but we chose to ignore j. In the SOAP request for d, we see the run-time data type of www. Further, the default values of both j and k are zero.

 

The last parameter 'e' to function abc, is of type yyy, but it is initialized to a www instance. The SOAP request reveals the type attribute of www for the last parameter, with its three child elements. The last two elements j and k, possess the value of 0.

 

Thus, the run time data type is more dependent upon the type of object that the variable is initialized to, rather than the static data type that is used while creating the variable. Also, the type attribute selects the child elements that are serialized or carried across the wire.

 

The next program illustrates how interfaces can be used in an asmx file. In the asmx file, the class yyy is derived from iii interface. In order to be technically on the right side of WSDL, the function abc is provided with the iii interface, together with the yyy object.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(iii i , yyy a)

{

}

}

public interface iii

{

}

public class yyy : iii

{

public int i;

}

 

 

The wsdl file shows two child elements in the element abc, containing the types iii and yyy. The complexType iii has an attribute called 'abstract', which is set to true. This indicates that no element can use this complexType directly. Further, only a complexType can derive from it. However, the default value is false.

 

Thus, the wsdl is an aide memoir or reminder to us that we are dealing with an interface, rubbing the fact in, that interfaces are incapable of holding fields directly.

 

aa.wsdl

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

  <s:element name="abc">

<s:complexType>

  <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" nillable="true" type="s0:iii" />

<s:element minOccurs="1" maxOccurs="1" name="a" nillable="true" type="s0:yyy" />

</s:sequence>

</s:complexType>

  </s:element>

  <s:complexType name="iii" abstract="true" />

  <s:complexType name="yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" type="s:int" />

</s:sequence>

  </s:complexType>

  <s:element name="abcResponse">

<s:complexType />

  </s:element>

</s:schema>

 

zzz.cs

public abstract class iii

{

}

[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://tempuri.org/")]

public class yyy

{

public int i;

}

 

The proxy file zzz.cs displays the interface converted into an abstract class iii. Also, the yyy class is not derived from the abstract class, but it contains the members of the interface. This substantiates the fact that the interface is not used in the program. All this leads to a sole inference that, at this stage, interfaces are totally forbidden in an asmx file.

 

After having touched upon the concept of interfaces, let us examine structures. In C#, the basic data types, such as int and char are termed as 'value data types', and not as 'reference data types'. They are employed wherever speed and efficiency are of prime importance. The struct data type in C# is a value data type. All value data types are created on the stack.

 

The function abc in the asmx file, accepts a single parameter of type yyy structure.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(yyy a)

{

}

}

public struct yyy

{

public int i,j;

}

 

The wsdl file has one child element 'a' of type yyy, which in turn, is a complexType having two elements, i and j. Thus, there is no dissimilarity whatsoever, between a class and a struct in the wsdl file. Furthermore, there is no entity that differentiates between a class and a struct, in the wsdl vocabulary.

aa.wsdl

<s:element name="abc">

 <s:complexType>

   <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="a" type="s0:yyy" />

  </s:sequence>

</s:complexType>

  </s:element>

  <s:complexType name="yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" type="s:int" />

<s:element minOccurs="1" maxOccurs="1" name="j" type="s:int" />

</s:sequence>

  </s:complexType>

  <s:element name="abcResponse">

<s:complexType />

  </s:element>

 

We now shift our focus upon the client program. Semantically, there is no dissimilitude between a class and a structure. As before, the 'new' keyword is employed to create the variable 'b', thereby, eliminating all the differences that a class may have from a struct. The yyy entity has all the looks and feel of a class.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

yyy b = new yyy();

b.i = 10;

b.j = 20;

a.abc(b);

}

}

 

The object 'b' can be created only with the help of the 'new' keyword.

 

In normal programming, a struct entity does not employ the 'new' keyword. Therefore, it is prudent not to use structures in the asmx file, since wsdl is devoid of any such notation, which may aid in describing structures. A wsdl file contains only value types.

 

The proxy that is generated, is also of the belief that the yyy entity is a class, and not a struct. With the exception of one small variation wherein, one extra Attribute named XmlElementAttribute has been added to the parameter 'a', everything else remains unaltered. The XmlElementAttribute attribute merely conveys that the parameter 'a' will be serialized as an element, and would not be considered as an attribute.

 

zzz.cs

public void abc([System.Xml.Serialization.XmlElementAttribute (IsNullable=false)] yyy a)

{

this.Invoke("abc", new object[] {a});

}

public class yyy

{

public int i;

public int j;

}

 

SOAP request

<abc xmlns="http://tempuri.org/">

- <a>

  <i>10</i>

  <j>20</j>

  </a>

</abc>

 

There is nothing exceptional about the SOAP request. The data values of 10 and 20 are sent across, as is always done.

 

The next program demonstrates how an array of objects is sent to and fro, while being used in an asmx file.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public void abc(yyy [] a)

{

}

}

public class yyy

{

public int i,j;

}

 

The function abc is granted an array of yyy objects as a parameter. The class yyy consists of two public variables i and j, of data type int.

 

The wsdl file displays the usual element 'a', of type ArrayOfyyy. This is the name of a type, and the actual name per se, is of no significance. Had it been named as 'bbb', in place of ArrayOfyyy, the complexType would have been of the same name, in that case.

 

aa.wsdl

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

  <s:element name="abc">

<s:complexType>

  <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="a" nillable="true" type="s0:ArrayOfYyy" />

  </s:sequence>

</s:complexType>

  </s:element>

  <s:complexType name="ArrayOfYyy">

<s:sequence>

<s:element minOccurs="0" maxOccurs="unbounded" name="yyy" nillable="true" type="s0:yyy" />

</s:sequence>

  </s:complexType>

  <s:complexType name="yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="i" type="s:int" />

<s:element minOccurs="1" maxOccurs="1" name="j" type="s:int" />

</s:sequence>

  </s:complexType>

</s:schema>

 

The type ArrayOfyyy is made up of a sequence of two int type elements i and j. They are allotted attributes, with values that sanction only one occurrence of each. However, the element 'a' may have as many occurrences as is deemed fit.

 

In the client program, an object 'b', which is an array of two yyy objects, is created using the 'new' keyword. Thereafter, the two yyy objects, b[0] and b[1], are created individually and their members are initialized. An array of objects when considered individually, is similar to a simple object. Thus, the objects b[0] and b[1] are treated in much the same way, as any other simple object.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

yyy [] b = new yyy[2];

b[0] = new yyy();

b[0].i = 1;

b[0].j = 2;

b[1] = new yyy();

b[1].i = 10;

b[1].j = 20;

a.abc(b);

}

}

 

zzz.cs

public void abc([System.Xml.Serialization.XmlArrayItemAttribute ("yyy", IsNullable=true)] yyy[] a)

{

this.Invoke("abc", new object[] {a});

}

public class yyy

{

public int i;

public int j;

}

 

The lone parameter 'a' in the proxy, has an attribute called XmlArrayItemAttribute. This attribute has the capability of serializing derived types. Normally, the attribute is used whenever the Xml Serializer has to serialize a derived class, after having specified the base class. 

 

SOAP request

<abc xmlns="http://tempuri.org/">

- <a>

- <yyy>

  <i>1</i>

  <j>2</j>

  </yyy>

- <yyy>

  <i>10</i>

  <j>20</j>

  </yyy>

  </a>

</abc>

 

Finally, in the SOAP payload, the child element 'a' is seen to contain two yyy children, which carry the data of the individual arrays.

 

The next program demonstrates as to how enums are handled in wsdl files.

 

An enum is purely a variable, which is capable of containing only a stipulated range of values. The enum yyy contains only two values, viz. aa and bb. The abc function in the asmx file accepts this enum.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

public yyy abc(yyy a)

{

return yyy.bb;

}

}

public enum yyy

{

aa = 1,

bb = 2

}

 

aa.wsdl

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

  <s:element name="abc">

<s:complexType>

  <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="a" type="s0:yyy" />

  </s:sequence>

</s:complexType>

  </s:element>

  <s:simpleType name="yyy">

<s:restriction base="s:string">

  <s:enumeration value="aa" />

  <s:enumeration value="bb" />

</s:restriction>

  </s:simpleType>

  <s:element name="abcResponse">

<s:complexType>

  <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="abcResult" type="s0:yyy" />

  </s:sequence>

</s:complexType>

  </s:element>

<s:element name="yyy" type="s0:yyy" />

</s:schema>

 

Most of the code in the wsdl file, upto the element 'a', remains unchanged. However, you would have noticed that 'simpletype' has been used in place of 'complex type', for the type yyy.

The 'restriction' element imposes an additional limitation over and above the current restriction, on the base type being a string. It permits only certain values within the string type. These values are specified by a child element enumeration. Since there are only two such enumeration elements 'aa' and 'bb', the simpleType yyy can contain only one of these two values.

 

Thus, an enum can be represented in wsdl, because the XML schema supports the concept of enumeration. The function returns an enum. Therefore, the abcResponse element does not remain empty. Observe the element called yyy, of type yyy, located at the very end. This element is not being utilized anywhere.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

System.Console.WriteLine(a.abc(yyy.aa));

}

}

 

Output

bb

 

zzz.cs

public yyy abc(yyy a)

{

object[] results = this.Invoke("abc", new object[] {a});

return ((yyy)(results[0]));

}

public enum yyy

{

aa,

bb,

}

 

SOAP request

<abc xmlns="http://tempuri.org/">

 <a>aa</a>

 </abc>

 

SOAP response

<abcResponse xmlns="http://tempuri.org/">

  <abcResult>bb</abcResult>

</abcResponse>

 

The client program a.cs merely accepts a member aa from the enum yyy, as a parameter to the abc function. Subsequent to this, it displays the return value. The abc function in the proxy has no surprises tucked up its sleeve. Here, the enum data type is seen without the values that we specified in the asmx file.

 

The values specified for the enum variables in the asmx file, do not get carried over to the proxy. It is so because they are available in the asmx file. Thus, if you observe the SOAP output, you will realize that it displays only the name of the enum 'aa' being sent across, and not its value. Also, the SOAP response elicits that the packet contains the enum name 'bb', and not its value.

 

The final example in this chapter deals with an XmlNode type as a parameter to the abc function. An XmlNode represents an XML document.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

using System.Xml;

public class zzz

{

[WebMethod]

public void abc(XmlNode a)

{

}

}

 

aa.wsdl

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

  <s:element name="abc">

<s:complexType>

  <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="a" nillable="true">

  <s:complexType mixed="true">

<s:sequence>

  <s:any />

</s:sequence>

  </s:complexType>

</s:element>

  </s:sequence>

</s:complexType>

  </s:element>

  <s:element name="abcResponse">

<s:complexType />

  </s:element>

</s:schema>

 

The abc function now receives an XML file in a SOAP payload. The wsdl file has an element 'a', which like before, is devoid of a type attribute. It has a complexType element that describes its type.

 

The attribute 'mixed' has a value of 'true', which permits character data for this type. The 'any' element permits the use of elements from a specified namespace. As no namespace attribute has been specified, the default gives access to elements that may belong to any namespace.

 

a.cs

using System.Xml;

public class aaa

{

public static void Main()

{

zzz a = new zzz();

XmlDocument d = new XmlDocument();

d.LoadXml("<vijay>"+ "<mukhi>Hi <sonal />Bye</mukhi>" + "</vijay>");

XmlNode e = d.DocumentElement;

a.abc(e);

}

}

 

The client program begins with a class that understands an Xml file called XmlDocument. The LoadXml member requires a string that represents an XML document.

 

There is a root element called 'vijay', which is followed by a valid Xml file. The property DocumentElement allows access to the XmlNode that represents this Xml file. This is furnished as a parameter to the abc function.

 

zzz.cs

public void abc(System.Xml.XmlNode a)

{

this.Invoke("abc", new object[] {a});

}

 

The proxy server understands XmlNode, since it can recognize any element in a wsdl file.

 

SOAP request

<abc xmlns="http://tempuri.org/">

<a>

<vijay xmlns="">

<mukhi>

  Hi

  <sonal />

  Bye

</mukhi>

</vijay>

</a>

</abc>

</soap:Body>

 

The SOAP request starts with the name of the parameter 'a', followed by the root tag 'vijay'. The default namespace is set to null. Thereafter, the entire Xml Document is passed as the content.

 

Thus, there is no difference between passing an int or an object or an Xml file in a SOAP packet, because they are both sent across as textual content.