An Extensive Examination of Web Services: Part 5
By Scott Mitchell
An Extensive Examination of Web Services Index
An Extensive Examination of Web Services is a multi-part article series spanning
several months. Listed below are the current "parts," along with a very brief synopsis and the
date published.
Part 1 - Examines the basics of Web services, what Web services are,
and the technologies and standards that serve as the underpinnings of Web services. (October 8th, 2003)
Part 2 - Examines creating Web services using Visual Studio
.NET. Looks underneath the hood of the code created by VS.NET. (October 15th, 2003)
Part 3 - Examines creating a client application that consumes
a Web service. Discusses the purpose and structure of a WSDL document, along with creating and using proxy
classes to consume a Web service. (November 5th, 2003)
Part 4 - Examines the utility of Web services and common
scenarios where Web services make sense. A business-oriented look at Web services. (November 19th, 2003)
Part 5 - Takes an in-depth look at XML serialization, which is the
process of converting a data type, such as an integer, array, or custom class, into its XML representation, and back again.
Every time a message is passed to or from a Web service, XML serialization transpires. (December 17th, 2003)
Part 6 - Looks at sending metadata to a Web method through the use of SOAP headers.
Examines defining and accepting a SOAP header on the Web service end, and looks at sending a populated SOAP header
from the client. (December 31st, 2003)
Part 7 - Examines how the incoming and outgoing messages to a Web
service can be programmatically modified via SOAP Extensions. (January 21st, 2004)
Part 8 - Learn about the Web Service Enhancements (WSE) and Microsoft's
free class library for implementing the WSE standards. (June 30th, 2004)
Part 9 - See how to implement UsernameToken authentication using
the WSE 2.0 Toolkit. (July 14, 2004)
Part 10 - Learn how to send large amounts of data as attachments using
DIME and WS-Attachments. (September 8th, 2004)
Introduction
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:
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 - x and
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 Int32
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 Int32 structures.
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 XmlSerializer class, be sure to read Anthony Hart's article
XML Serialization in ASP.NET.
XML serialization is preformed by the XmlSerializer
class. This class has two important methods: Serialize(stream, object) and
Deserialize(stream). The 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
serialized via 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 XmlSerializer class can, additionally, serialize complex data types,
such as collections and custom classes. Since Web services use XmlSerializer this 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 CardNumber,
ExpirationMonth, and ExpirationYear. The following code shows the Web service class, with
its method MakeDebit() method that takes in two parameters: a CreditCardInfo instance and
an amount.
public class MyWebService : System.Web.Services.WebService
{
[WebMethod()]
public void MakeDebit(CreditCardInfo ccInfo, float amount)
{
// ... process the credit card ...
}
}
public class CreditCardInfo
{
private string _number;
private int _expMonth, _expYear;
public string CardNumber
{
get { return _number; }
set { _number = value;}
}
public int ExpirationMonth
{
get { return _expMonth; }
set { _expMonth = value;}
}
public int ExpirationYear
{
get { return _expYear; }
set { _expYear = value;}
}
}
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 wsdl.exe
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 CreditCardInfo.
Also created on the client would be a class named CreditCardInfo.
The client could call MakeDebit() with the following few lines of code:
// create an instance of the proxy class
MyWebService proxy = new MyWebService();
// create an instance of the CreditCardInfo object
CreditCardInfo cc = new CreditCardInfo();
cc.CardNumber = "1111222233334444";
cc.ExpirationMonth = 2;
cc.ExpirationYear = 2005;
// call MakeDebit()
proxy.MakeDebit(cc, 4.95F);
The XML serialization process would serialize the CreditCardInfo class instance in this example to the
following XML:
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
CreditCardInfo.
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
get accessor, and not a set accessor 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 System.Collections.CollectionBase
adding an 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
of type CreditCardInfo, like so:
[WebMethod()]
public void FooBar(CreditCardInfo [] collectionOfCCInfos)
{
...
}
Another option is to create a strongly-typed collection class derived from System.Collections.CollectionBase.
You'd then pass in an instance of this strongly-typed collection class. For example, you'd first create the class:
public class CreditCardInfoCollection : System.Collections.CollectionBase
{
public void Add(CreditCardInfo cc)
{
this.InnerList.Add(cc);
}
public CreditCardInfo this[int index]
{
get { return (CreditCardInfo) this.InnerList[index]; }
set { this.InnerList[index] = value; }
}
}
And then you'd have the Web service method accept an input of this strongly-typed collection class:
[WebMethod()]
public void FooBar(CreditCardInfoCollection collectionOfCCInfos)
{
...
}
Conclusion
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 XmlSerializer class. Along with simple, scalar data types,
the XmlSerializer class 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.
Beginner's .NET XML Web Services offers nearly eight hours of training
on .NET XML Web services in a video format. This two-disc DVD set, presented by
author Scott
Mitchell, offers a unique opportunity for learning about the fundamentals of Web services.
The 14 lessons begin with an examination of the core Web service standards, and then quickly move
into showing you how to create and consume Web services in Microsoft's .NET Framework. There are in-depth
lessons for each of the core Web service standards - XML, SOAP, and WSDL - along with examples of the
Web Service Enhancements (WSE). Scattered throughout each of
these chapters are extensive demos, depicting how to build, deploy, and access Web services using
Microsoft's Visual Studio .NET. The DVD also contains a thorough examination of a real-world,
end-to-end Web service application.
Scott Mitchell is the editor and founder of 4GuysFromRolla.com, author of the An Extensive Examination
of the Web Services article series, and author of numerous ASP and ASP.NET
books.