Introduction
About a year ago, I became very enthusiastic. I found a new attribute in the framework which allows me to determine the WSDL contract which describes my objects. Used in conjunction with the IXmlSerializable
interface, I can also make sure the serializer pumps out the correct XML, corresponding to the custom XML-Schema inside the WSDL. Why is this so important? The default XmlSerializer
requires all fields to have public get
/ set
properties. This means my Product
object needs a public get
/set ID
property. I personally didn’t want a settable ID
property, because it is the never-changing primary key for that object. Furthermore, I did not want to change the public
interface of my objects just because they’ll be serialized into a SOAP message. Using XmlSchemaProvider
with IXmlSerializable
, you can fix all this; keep only the getter, and still serialize the ID.
Now, all of this sounds good of course, and there are various samples on the internet showing how you can use these two classes to serialize your own ‘Product
’ objects. However, the examples I found only show how to serialize a single object, not a tree of objects, such as an Order
-OrderItem
combination or Order
-Customer
-Address
. Is this more difficult then? Well, yeah, a tad-bit, because you’ll have to share the XML-Schema definition somehow.
Not finding the answer on the web, I dug into this problem myself, like I did about a year ago. The only difference is that this time, I have a working solution which I want to share with you guys.
Serializing an Order Object
The following example will show how you can utilize XmlSchemaProvider
for serializing an object-tree. This example uses two objects, Order
and Product
, with an Order
containing multiple Product
s. The Product
class contains a readonly
ID property, and has a private
default constructor. The Order
class contains a readonly List<T>
of Product
s and also a readonly ID
property. Here, I’ll give attention only to the Order
class, the Product
class is not that interesting after reading up on the subject. Check the code to see how it has been built.
The Order
class I’ll describe a bit further, since that’s where the magic happens.
The basic class definition for Order
is as follows:
[XmlSchemaProvider("GetOrderSchema")]
public class Order : IXmlSerializable
{
int _id;
List<Product> _products = null;
public int ID { get;}
public List<Product> Products { get;}
}
To supply the correct schema to the serialization infrastructure, the XmlSchemaProvider
attribute is applied to the class. Inside the method tied to the XmlSchemaProvider
attribute, the schema for the Order
class is constructed using the System.Xml
object model, also the Product
is included in the schema for the Order
class using an XmlSchemaInclude
:
public static XmlSchemaComplexType GetOrderSchema(
XmlSchemaSet schemas)
{
string tns = "http://diveinit.nl/schemas/test";
string xmlns = "http://www.w3.org/2001/XMLSchema";
XmlSchema schema = new XmlSchema();
XmlSchema productSchema = Product.Schema;
XmlSchemaInclude productSchemainclude =
new XmlSchemaInclude();
productSchemaInclude.Schema = productSchema;
schema.Includes.Add(productSchemaInclude);
...
...
}
Now that the WSDL has been set up correctly, it is time to serialize and deserialize the Order
class. This is done using the IXmlSerializable
interface, which requires you to build three methods. First up is the serialization using the WriteXml
method. This method is also responsible for serializing the Product
objects contained in the Order
.
void IXmlSerializable.WriteXml(XmlWriter writer)
{
string ns = "http://diveinit.nl/schemas/test";
writer.WriteElementString("id", ns, _id.ToString());
ICollection<Product> products = Products;
if (products.Count > 0)
{
writer.WriteStartElement("ProductCollection");
foreach (Product product in products)
{
writer.WriteStartElement("Product");
((IXmlSerializable)product).WriteXml(writer);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
The root element is supplied for you, this means you don’t have to write this yourself inside the WriteXml
method. So the first item to write is the private ID
field of the Order
object. Next up are all the Product
s contained in the Order
. They are contained inside an extra element called "ProductCollection
". Make sure to use the WriteXml
method of the Product
object so that is serialized correctly too.
Now for some deserialization. This is a bit more difficult because you do not know how many Product
s the Order
contains, when deserializing. The XmlReader
provides a handy method to determine this, which is called IsStartElement
.
void IXmlSerializable.ReadXml(XmlReader reader)
{
string ns = "http://diveinit.nl/schemas/test";
reader.ReadStartElement();
_id = reader.ReadElementContentAsInt("id", ns);
if (reader.IsStartElement("ProductCollection", ns))
{
reader.ReadStartElement();
while (reader.IsStartElement("Product", ns))
{
Product product = new Product();
((IXmlSerializable)product).ReadXml(reader);
Products.Add(product);
}
}
reader.ReadEndElement();
}
That is it. A fully functional example on how to use XmlSchemaProvider
in conjunction with IXmlSerializable
for all your serialization needs!
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.