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.