I've run into a few gotchas when doing C# XML serialization that I thought I'd share:
- You can't serialize items that are read-only (like KeyValuePairs)
- You can't serialize a generic dictionary. Instead, try this wrapper class (from http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx):
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
return;
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
reader.ReadStartElement("item");
reader.ReadStartElement("key");
TKey key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadStartElement("value");
TValue value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
this.Add(key, value);
reader.ReadEndElement();
reader.MoveToContent();
}
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
foreach (TKey key in this.Keys)
{
writer.WriteStartElement("item");
writer.WriteStartElement("key");
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
writer.WriteStartElement("value");
TValue value = this[key];
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
writer.WriteEndElement();
}
}
}
Any other XML Serialization gotchas out there?
You can't serialize read-only properties. You must have a getter and a setter, even if you never intend to use deserialization to turn XML into an object.
For the same reason, you can't serialize properties that return interfaces: the deserializer wouldn't know what concrete class to instantiate.
If your XML Serialization generated assembly is not in the same Load context as the code attempting to use it, you will run into awesome errors like:
The cause of this for me was a plugin loaded using LoadFrom context which has many disadvantages to using the Load context. Quite a bit of fun tracking that one down.
If your XSD makes use of substitution groups, then chances are you can't (de)serialize it automatically. You'll need to write your own serializers to handle this scenario.
Eg.
In this example, an Envelope can contain Messages. However, the .NET's default serializer doesn't distinguish between Message, ExampleMessageA and ExampleMessageB. It will only serialize to and from the base Message class.
IEnumerables<T>
that are generated via yield returns are not serializable. This is because the compiler generates a separate class to implement yield return and that class is not marked as serializable.See "Advanced XML Schema Definition Language Attributes Binding Support" for details of what is supported by the XML Serializer, and for details on the way in which the supported XSD features are supported.
When serializing into an XML string from a memory stream, be sure to use MemoryStream#ToArray() instead of MemoryStream#GetBuffer() or you will end up with junk characters that won't deserialize properly (because of the extra buffer allocated).
http://msdn.microsoft.com/en-us/library/system.io.memorystream.getbuffer(VS.80).aspx