I`ve been having a lot of fun playing around with all the new goop in the .NET Framework
2.0 recently. Although my focus has been mostly on what`s new in the ASMX stack, I`ve
also been looking at XML serialization.
As part of my demo code for TechEd 2005, I`ll be showing how to implement the interface, System.Xml.Serialization.IXmlSerializable for
a type. This interface allows you to control the XML serialization/deserialization
process in a streaming context. Also, I`ll be showing how to implement an XML schema
provider. An XML schema provider is basically a method you delegate that controls
how your schema will look when it`s generated by tools like wsdl.exe.
On the surface, XML serialization and XML schema generation aren`t too difficult.
That is, until you try to do something a little more funky.
I`ve hit a snag in my implementation for a XML schema provider. The purpose of this
blog entry is to solicit any help from you (the reader). As it stands right now, I`m
stuck and can`t seem to find an adequate solution. But before I describe my problem,
I should probably provide some context for those of you who don`t know what I`m talking
about.
The .NET Framework 2.0 dramatically improves the infrastructure for controlling XML
serialization and XML schema generation. It achieves this by supporting the interface, System.Xml.Serialization.IXmlSerializable.
Some of you will recognize this interface. It`s the interface used by the System.Data.DataSet to
control its serialization process to and from XML. In the .NET Framework 1.0/1.1,
this interface was unsupported and undocumented. However, a few brave souls were able
to figure out that this interface was ideal for controlling the XML serialization
process. By implementing this interface, developers had finer control over how this
process occurred.
At this point you might be asking, "What about all the XML serialization attributes
we have Can`t they do the job" The answer is yes and no. Yes, the XML serialization
attributes found in the System.Xml.Serialization (i.e. XmlTypeAttribute, XmlIgnoreAttribute)
can do the job. However, they are somewhat limited. Let`s take a look at an example.
Assume the following class:
public class Person
{ string _name; public string Name
{ get; set; } }
Without any modification, an instance of this type will serialize as follows:
<
Person>
<Name>...
Name>
Person>>
>
Now, what if I want to modify how the XML looks Well, I can simply place an
XML serialization attribute on the class like so:>
public class Person
{ string _name; [XmlElement("PersonName")] public string Name
{ get; set; } }
Now when the XML serialization occurs, we get the following XML:
<Person>
<PersonName>...PersonName>
Person>
Clean. Simple. Easy. Good enough, right Well, sort of...
What if I wanted to change the way the XML in the following manner
<Person>
<FirstName>...FirstName>
<Surname>...Surname>
Person>
Without directly modifying the type, I`m screwed. The XML serialization attributes
don`t provide enough control. Enter the IXmlSerializable interface.
The IXmlSerializable interface is a relatively simple interface:
namespace System.Xml.Serialization
{ public interface IXmlSerializable
{ XmlSchema GetSchema(); void ReadXml(XmlReader reader); void WriteXml(XmlWriter
writer); } }
The two methods we`re most concerned about are ReadXml and WriteXml.
These methods will provide you the ability to control the serialization of an
object graph into XML and vice versa. GetSchema is a method that
is no longer used; rather, the .NET Framework (specifically, the System.Xml.Serialization.XmlSerializer class)
leverages an XML schema provider. I`ll explain XML schema providers in just a second.
For now, let`s look at an example of what I`m talking about:
public class Person
: IXmlSerializable { string _name; public void XmlSchema
GetSchema() { return null; } public void ReadXml(XmlReader
reader) { while (reader.Read()) { if (reader.NodeType
== XmlNodeType.Element) { if (reader.Name == "FirstName" &&
reader.Read()) _name = (string) reader.Value; if (reader.Name
== "Surname" && reader.Read()) _name += "
" + (string) reader.Value; } } } public void WriteXml(XmlWriter
writer) { writer.WriteElementString("FirstName") writer.WriteElementString("Surname")
} public string Name { get; set; }
}
In this example, I`ve elected to modify the XML that would have been generated. Here`s
what the resulting XML looks like:
<Person>
<FirstName>...FirstName>
<Surname>...Surname>
Person>
Pretty nice, eh Now, let`s talk about XML schema providers.
XML schema providers are essentially methods that are delegated for providing the
XML Schema representation for a type. These methods are specified by the System.Xml.Serialization.XmlSchemaProviderAttribute attribute.
Applying this attribute to the Person class (above) looks like this:
[XmlSchemaProvider("GetPersonSchema") public class Person
: IXmlSerializable { string _name; public static XmlQualifiedName
GetPersonSchema(XmlSchemaSet schemaSet) { XmlSchema schema = new XmlSchema();
schema.TargetNamespace = "http://www.bristowe.com/"; XmlSchemaComplexType
personType = new XmlSchemaComplexType(); personType.Name = "Person";
XmlSchemaSequence sequence = new XmlSchemaSequence(); personType.Particle
= sequence; XmlSchemaElement firstNameElement = new XmlSchemaElement();
firstNameElement.Name = "FirstName"; firstNameElement.SchemaTypeName
= new XmlQualifiedName("string", XmlSchema.Namespace);
sequence.Items.Add(firstNameElement); XmlSchemaElement surnameElement = new XmlSchemaElement();
surnameElement.Name = "Surname"; surnameElement.SchemaTypeName
= new XmlQualifiedName("string", XmlSchema.Namespace);
sequence.Items.Add(surnameElement); schema.Items.Add(personType); schemaSet.Add(schema); return new XmlQualifiedName("Person", "http://www.bristowe.com/");
} public void XmlSchema GetSchema()
{ return null; } public void ReadXml(XmlReader
reader) { while (reader.Read()) { if (reader.NodeType
== XmlNodeType.Element) { if (reader.Name == "FirstName" &&
reader.Read()) _name = (string) reader.Value; if (reader.Name
== "Surname" && reader.Read()) _name += "
" + (string) reader.Value; } } } public void WriteXml(XmlWriter
writer) { writer.WriteElementString("FirstName") } public string Name
{ get; set; } }
So, what does this do Well, when tools like xsd.exe or wsdl.exe request the XML Schema
for this type, they first check to see if a XML schema provider has been indicated
by the type. If so, the method will be invoked. Otherwise, the
schema will be inferred. For the code sample listed above, the following XML
Schema is generated:
<
complexType name="Person">
<sequence>
<element name="FirstName" type="xsd:string" />
<element name="Surname" type="xsd:string" />
sequence>
complexType>>
>
So, by implementing
IXmlSerializable and providing a XML schema
provider, you have full control over the XML serialization of your types and their
associated schema.>
Here`s my problem: While implementing a XML schema provider, I run into the problem
of generating the schema for a type that contains a System.Guid structure.
Now, I realize that the Guid is a xsd:string-based restriction simple
type defined automatically by the runtime. However, I`m having trouble leveraging
this type inside my XML schema provider for my type. That is, the runtime - specifically,
the ASP.NET HTTP Runtime - is complaining that it cannot resolve the type, Guid which
resides in the namespace "http://microsoft.com/wsdl/types".
I`d love to find a solution to this problem. So far, nothing. Anyone Anyone Bueller