An Extensive Examination of Web Services: Part 5By Scott Mitchell
Welcome to the 5th installment of an Extensive Examination of Web Services. In Part 1 we examined the basics of Web services, looking at what, precisely, standards made up Web services. In this first article we discussed SOAP, which stands for Simple Object Access Protocol, and is the standard used for formatting the messages passed between the client consuming the Web service, and the Web service itself. Recall that invoking a Web service involves two messages:
- A request message, in which a client invokes a particular method of the Web service, passing in input parameters, if applicable, and
- A response message, in which the Web service indicates that the method has been called, and returns the method's return value, if applicable.
As we looked at in Part 1, a SOAP-formatted request message has the following form:
While the SOAP-formatted response message has the following form:
Notice that the request message can have a variable number of input parameters, and the response can have an optional result
value (the method's return value). Both the input and output parameter values are formatted as XML. For example,
a Web service method called
Add that took in as input two integers inputs -
y - and returned the sum as an integer, would
have the following request and response messages if the client wished to determine the sum of 5 and 8:
Notice that the client and the server are sending and receiving the input and output parameter values as XML.
However, .NET does not store an integer in memory as XML markup; rather, it stores as integer as an
structure. This implies that at some point the client, when calling the
Add() method, must convert the
integers 5 and 8 into an XML representation. Similarly, when the Web service receives the XML representation of 5 and
8 it must convert this XML representation back into
This process is known as message serialization and message deserialization, respectively, and is the focus of this fifth part of the article series. In this article we'll look at the serialization and deserialization process, and see how even complex types - such as custom classes and arrays - can be effortlessly serialized and deserialized.
An Overview of XML Serialization
When working with data in a computer program, typically the data is located in the memory (RAM) of the computer on which the program is running. For example, when you are creating a letter in Microsoft Word, the contents of your letter, the formatting, and much other miscellaneous information is stored in the computer's memory. Now, imagine that you wanted to save the Word document to a file. How is this accomplished?
Clearly there needs to be some way for the data to be persisted. But the format of the data in the computer's memory may not be the ideal format to have it stored on the hard drive. Therefore there needs to be some conversion from the data's representation in memory to a representation suitable for storing it on disk. The process of converting data from one format to another is known as serialization. Once data has been serialized from one format to another, we typically will want to, at some later point in time, convert it back to the original format. This inverse operation is referred to as deserialization.
The .NET Framework provides a number of serialization options. The one that is pertinent to Web services is XML serialization, which involves serializing in-memory data to an XML format. With Web services this XML-formatted version of the data is sent from the client consuming the Web service, to the Web service, or vice-a-versa. The nice thing about the .NET Framework's XML serialization capabilities is that we, the developer, have to do very little, if anything, to benefit from it.
|Persisting Data to Disk with XML Serialization|
XML serialization is not limited in use to just Web services. For example, you can persist an object to disk using
XML serialization. This could be used in a WinForms application to save data to the hard drive in a format that's
easily viewable and modifiable by power users. For more information on XML serialization and some code samples of
explicitly using the |
XML serialization is preformed by the
class. This class has two important methods:
Serialize(stream, object) and
Serialize() method takes in a stream and an object instance;
it converts the object into its XML representation and squirts it out to the passed-in stream. Conversely, the
Deserialize() method takes in a stream instance with the XML-formatted data, and deserializes it, returning
an object instance.
When calling a Web service through the auto-generated proxy class, the data types being passed to the Web service are automatically
XmlSerializer. Similarly, when the serialized data reaches the Web service it is automatically
deserialized before being handed off to the called Web method. Inversely, when the Web methods sends its return value,
that value is automatically serialized into XML, sent back to the client, and is then deserialized automatically.
The figure below illustrates this concept:
Serializing Complex Data Types
As we've seen with some of the simple Web service examples we've examined throughout this article series thus far, it is apparent that the .NET Framework's XML serialization can serialize simple data types, like strings, integers, floats, date/times, and so forth. The
XmlSerializerclass can, additionally, serialize complex data types, such as collections and custom classes. Since Web services use
XmlSerializerthis means we can pass complex data types effortlessly to and from Web services!
For example, imagine that we wanted to create a Web service method that accepted an input parameter of type
CreditCardInfo, which was a class defined with public properties like
ExpirationYear. The following code shows the Web service class, with
MakeDebit() method that takes in two parameters: a
CreditCardInfo instance and
Notice that after the Web service class we provide our definition of the
CreditCardInfo class. This
class is relatively straightforward - it has three public properties as discussed earlier. When creating a client to
consume this Web service, you would create the proxy class through Visual Studio .NET or using
as discussed in Part 3 of this article series.
The proxy class created on the
client would contain a method called
MakeDebit() that took in an object of type
Also created on the client would be a class named
The client could call
MakeDebit() with the following few lines of code:
The XML serialization process would serialize the
CreditCardInfo class instance in this example to the
This information, along with the second input parameter (
amount), would be packaged into a SOAP-formatted
message and sent to the Web service via an HTTP request. The Web service, upon receiving this request, would deserialize
the input parameters, converting the
CreditCardInfo XML content back into an in-memory instance of type
Some Closing Notes on XML Serialization
There are a few subtle details with XML serialization that are important to know about when working with Web services that accept complex data types. Realize that XML serialization:
- XML serialization can only be applied to classes that contain a public default (parameterless) constructor. If you create a class in the Web service that does not have a public default constructor, the Web service will still compile, but when attempting to generate the proxy class on the client's end you'll receive an error message.
- Read-only class properties are not serialized. That is, if a property in the class has only a
getaccessor, and not a
setaccessor as well, the property will not be serialized.
- Only public properties and fields are serialized. Private properties are not serialized.
- To serialize a strongly-typed collection of objects, have the class derived from
Add()method and an indexer. (See this article for more information.) Alternatively you can send an array of the specified type.
On the last point, consider the case where you want a Web service method to accept a set of custom class instances.
For example, we looked at a custom
CreditCardInfo class - imagine you wanted to build a Web method that
accepted a number of instances of type
CreditCardInfo. One option is to have the method accept an array
CreditCardInfo, like so:
Another option is to create a strongly-typed collection class derived from
You'd then pass in an instance of this strongly-typed collection class. For example, you'd first create the class:
And then you'd have the Web service method accept an input of this strongly-typed collection class:
In this article we examined the process by which input and output parameter values to a Web service method are converted to and from an XML format. This process is known as XML serialization and deserialization, and (with Web services) is handled for us behind the scene with the
XmlSerializerclass. Along with simple, scalar data types, the
XmlSerializerclass can also serialize and deserialize complex data types, such as arrays and custom classes. Thanks to this power, we can effortlessly create Web services that accept non-trivial data types.