Introduction
It is good practice to design the interface to a web service by applying the same standards that would normally be applied to an XML schema. It should be versioned, extensible, and be well structured, allowing for good reuse.
Background
Within a Web Service, all the messages are described using an XML schema (XSD), so it makes sense to design our interface in terms of an XML schema, and then embed this into our web service.
The following example shows the messages we would like our simple web service to exchange. Our simple Web Service will provide information about a given product, when provided with a product code. We will also need to provide login details to the service.

The information returned describes the product, pricing, and stock levels.

So, we would like to build a web service with a web method that takes a ProductDetailsRequestType and returns a ProductDetailsResponseType.

So that’s what we’re after. Now, let's look at how to achieve this.
Using the standard .NET Framework serialization
We could create a class for each entity in the schema, give them the appropriate properties, and let the standard .NET framework [WebMethod] serialisation do the rest. This is an acceptable approach for small projects, but if you want to pass complex objects or objects from 3rd party standards (HL7, FpML, ebXML, etc.), this starts to become overwhelming.
Another approach I've seen used a lot is just to pass the XML data via a string parameter. This has a number of drawbacks, the XML will all be escaped so it expands (< becomes < etc.). Also, the web service description (WSDL) now tells you nothing useful about the web service - it takes in a string and returns a string. Not a great deal of help for implementers. Also, your web service provides no validation, your web service method will get called regardless of the content of the string - it doesn't even have to be XML.
A better approach is to use an XmlElement as the in/out parameter to your web service. This solves some of the problems, the XML is not escaped, your method only gets called if the XML is valid (well formed), but implementers still have no idea what the web service data is supposed to look like, and you only get minimal validation performed on your XML.
So wouldn't it be useful to be able to pass an XmlElement via a web service and have them fully described in the WSDL.
Customizing the serialization performed by XmlElement
In order to pass an XmlElement as a web service parameter and have it fully described within the WSDL, we need to create a class to wrapper an XmlElement. This wrapper class will then be passed instead of the XmlElement. Our wrapper class must implement the IXmlSerializable interface in order to allow the framework to read and write XML to/from the object, and have its own XmlSchemaProvider attribute to allow it to describe itself in terms of a XML Schema within the WSDL.
In order for the framework to be able to correctly describe the web service, we first need to tell it about the schema. This is done by adding the XmlSchemaProvider attribute to the wrapper class. The XmlSchemaProvider attribute tells the framework that information about the schema can be obtained by calling the “MySchema” method.
Now, when the framework is trying to describe the web service, it will call the MySchema method. The framework expects our implementation of MySchema to add all the schemas it needs to know about into the schema set collection; so, if you are working with elements from external schemas (HL7, FpML etc.), then you can add these to the schemas collection as well.
The return parameter from MySchema is the fully qualified name of the type within our XSD that describes the parameter. Note this must be a complex type, the .NET framework will not allow you to specify an element.
Because the 'MySchema' method is static, one wrapper class must be created for each type of object being passed, as the return value tells the .NET framework which complex type in the schema this argument represents.
A further improvement on this would be to add validation to the element prior to serialisation, thus ensuring that the XML was actually valid before reading or writing it.
[XmlSchemaProvider("MySchema")]
public class MyXmlElement : IXmlSerializable
{
private XmlElement _element = null;
public MyXmlElement() {} public MyXmlElement(XmlElement xmlElemnt) { _element = xmlElemnt; }
public static XmlQualifiedName MySchema(XmlSchemaSet xs)
{
if (xs.Schemas("http://www.liquid-technologies.com/ProductDetailsSample").Count == 0)
{
using (FileStream fs = new FileStream(@"ProductEnquiry.xsd", FileMode.Open))
{
XmlSchema s = XmlSchema.Read(fs, null);
xs.Add(s);
}
}
return new XmlQualifiedName("ProductDetailsRequestType",
"http://www.liquid-technologies.com/ProductDetailsSample");
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(System.Xml.XmlReader reader)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(reader);
_element = xmlDoc.DocumentElement;
}
public void WriteXml(System.Xml.XmlWriter writer)
{
_element.WriteTo(writer);
}
#endregion
}
[WebMethod(Description = "Showing the technique with XmlElement.")]
public MyXmlElement Test()
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(@"SampleData.xml");
return new MyXmlElement(xmlDoc.DocumentElement);
}
The XML data binding approach
Another approach to the creation of Web Services is XML Data Binding. This provides a simple way to deal with XML within your application. This involves the automatic creation of a class for every entity in your schema. These classes are bound to the structure of the schema; so using our example, we would end up with an object called 'Credentials', with properties called 'Username' and 'Password'. This removes any notion of dealing with XML from the application, and means the developer just has to deal with objects, properties, and collections, all strongly typed, and enforcing the structure described in the XML schema.
These objects can be generated using a data binding tool. The following tools will generate the binding classes from your xml schemas
Once we have generated data binding classes, we can put together our web service. The first thing to do is declare our web method.
namespace ProductEnquiryWebService
{
[WebService(Namespace =
"http://www.MyCompany.com/ProductDetailsSample")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class Service1 : System.Web.Services.WebService
{
[WebMethod(Description = "Gets the details about a given product.")]
public ProductDetailsResponse
RequestProductDetails(ProductDetailsRequest productRequest)
{
ProductDetailsResponse response = new ProductDetailsResponse();
response.ProductDetails.Name = "Widget";
response.ProductDetails.Description =
"The important widget that goes between" +
" the flange and the spindle";
response.ProductDetails.Price = 25.36;
response.ProductDetails.StockLevel = 15000;
return response;
}
}
}
If we left things here, then the .NET framework would use its default serializer on our ProductDetailsRequest and ProductDetailsResponse objects. This would produce valid XML but would not describe input and output parameters in the WSDL. In order to do this, we must tell the framework about the structure of the ProductDetailsRequest and ProductDetailsResponse objects in terms of an XML Schema (xsd). This is done in the same way as before using the XmlSchemaProvider attribute.
[XmlSchemaProvider("MySchema")]
public class ProductDetailsRequest
{
public static XmlQualifiedName MySchema(XmlSchemaSet xs)
{
if (xs.Schemas("http://www.MyCompany.com" +
"/ProductDetailsSample").Count == 0)
{
using (FileStream fs =
new FileStream(@"ProductEnquiry.xsd", FileMode.Open))
{
XmlSchema s = XmlSchema.Read(fs, null);
xs.Add(s);
}
}
return new XmlQualifiedName("ProductDetailsQueryType",
"http://www.MyCompany.com/ProductDetailsSample");
}
...... The rest of the XML Data Binding Class ....
}
Testing your Web Service
Because your web service contains complex parameters, it is not possible to call it via a simple Web Service Explorer.

So, in order to call it, you need to either build yourself a test harness or use a test tool. Liquid XML Studio contains a handy tool for calling web services. This will automatically generate the request envelope and message, allowing you to modify and call it.

Sample request
="1.0" ="utf-8"
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<tnsa:RequestProductDetails xmlns:tns="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tnsa="http://www.liquid-technologies.com/ProductDetailsSample"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<tnsa:productRequest>
<tnsa:Credentials>
<tnsa:Username>Joe</tnsa:Username>
<tnsa:Password>password</tnsa:Password>
</tnsa:Credentials>
<tnsa:ProductSerialNo>0123456798</tnsa:ProductSerialNo>
</tnsa:productRequest>
</tnsa:RequestProductDetails>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Sample response
="1.0" ="utf-8"
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<RequestProductDetailsResponse
xmlns="http://www.liquid-technologies.com/ProductDetailsSample">
<RequestProductDetailsResult>
-->
<AA:ProductDetailsResponse
xmlns:AA="http://www.liquid-technologies.com/ProductDetailsSample">
<AA:ProductDetails>
<AA:Name>Widget</AA:Name>
<AA:Description>The important widget that goes
between the flange and the spindle</AA:Description>
<AA:Price>25.36</AA:Price>
<AA:StockLevel>15000</AA:StockLevel>
</AA:ProductDetails>
</AA:ProductDetailsResponse>
</RequestProductDetailsResult>
</RequestProductDetailsResponse>
</soap:Body>
</soap:Envelope>
Examining the WSDL
Because we have taken control of the serialisation process, the WSDL describing the web service contains the contents of the schemas that you added in via the MySchema method.
The WSDL Schema shown graphically.

The Diagram is from the Web Service Call Composer in the free edition of Liquid XML Studio
Conclusion
If you are building web services that require a hierarchy of objects to be sent or received, especially if those messages contain sections from existing schemas (formal standards or internal data structures) or contain complex constructs (inheritance, substitution groups etc.), then you need more control over the WSDL that defines your web service.
It is possible to exercise this level of control by passing wrappered XmlElement objects as parameters, but the use of XML data binding classes provides a much better way in which to work, simplifying the reading and writing of your XML data, and providing a framework of strongly typed objects in which to work, while also providing information to the .NET Web Service Framework that allows it to fully describe even the most complex data structures accurately within the WSDL.
Using this approach makes it possible to include parts of complex standards within your Web Service, and still have them properly described in the WSDL, enabling other tools to properly understand and use the interface.
Resources
History
- Created: 12/1/2009.
- Updated: 1/2/2009.