using System.Collections.Generic;
using System.Xml.Serialization;
using System.Diagnostics;
public class Rates : List<Rate>, IXmlSerializable
public Rates() : base() { }
public Rates(IEnumerable<Rate> collection) : base(collection) { }
#region IXmlSerializable Members
XmlSchema IXmlSerializable.GetSchema()
void IXmlSerializable.ReadXml(XmlReader reader)
XmlKeyValueListHelper.ReadXml(reader, this, null, s => new Rate { RateValue = s });
void IXmlSerializable.WriteXml(XmlWriter writer)
XmlKeyValueListHelper.WriteXml(writer, this, (i, rate) => "rate" + XmlConvert.ToString(i), r => r.RateValue);
public static class XmlKeyValueListHelper
const string XsiNamespace = @"http://www.w3.org/2001/XMLSchema-instance";
const string XsiNil = "nil";
public static void WriteXml<T>(XmlWriter writer, IEnumerable<T> collection, Func<int, T, string> encodeName, Func<T, string> encodeValue)
foreach (var item in collection)
writer.WriteStartElement(XmlConvert.EncodeLocalName(encodeName(i, item)));
writer.WriteAttributeString(XsiNil, XsiNamespace, XmlConvert.ToString(true));
writer.WriteValue(encodeValue(item) ?? "");
writer.WriteEndElement();
public static void ReadXml<T>(XmlReader reader, ICollection<T> collection, Func<int, string, bool> decodeName, Func<string, T> decodeValue)
if (reader.IsEmptyElement)
reader.ReadStartElement();
while (reader.NodeType == XmlNodeType.Element)
var key = XmlConvert.DecodeName(reader.Name);
if (decodeName == null || decodeName(i, key))
var nilValue = reader[XsiNil, XsiNamespace];
if (!string.IsNullOrEmpty(nilValue) && XmlConvert.ToBoolean(nilValue))
collection.Add(default(T));
if (reader.IsEmptyElement)
value = reader.ReadElementContentAsString();
collection.Add(decodeValue(value));
public Rates Rates { get; set; }
[XmlElement("zTestValue")]
public string ZTestValue { get; set; }
public static void Test()
Test(new Main { ZTestValue = "test value", Rates = new Rates(new[] { new Rate { RateValue = "101" }, null, new Rate(), new Rate { RateValue = "" }, new Rate { RateValue = "202" }, null }) });
Test(new Main { ZTestValue = "test value", Rates = new Rates() });
Test(new Main { ZTestValue = "test value", Rates = new Rates(new [] { new Rate { RateValue = "101" }, new Rate(), new Rate { RateValue = "" }, new Rate { RateValue = "202" } } )});
static void Test(Main main)
ConsoleAndDebug.WriteLine(xml);
var main2 = xml.LoadFromXML<Main>();
if (!(main.Rates.Select(r => r == null ? "" : r.RateValue ?? "").SequenceEqual(main2.Rates.Select(r => r == null ? "" : r.RateValue ?? ""))))
throw new InvalidOperationException("!(main.Rates.Select(r => r.Rate).SequenceEqual(main2.Rates.Select(r => r.Rate)))");
ConsoleAndDebug.WriteLine("Original and deserialized Main are identical.");
var xml2 = main2.GetXml();
if (!XNode.DeepEquals(XElement.Parse(xml), XElement.Parse(xml2)))
throw new InvalidOperationException("!(main.Rates.Select(r => r.Rate).SequenceEqual(main2.Rates.Select(r => r.Rate)))");
ConsoleAndDebug.WriteLine("Serialized and re-serialized XML are identical.");
ConsoleAndDebug.WriteLine("");
public static void Main()
public static class XmlSerializationHelper
public static T LoadFromXML<T>(this string xmlString)
using (StringReader reader = new StringReader(xmlString))
return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
public static string GetXml<T>(this T obj, bool omitStandardNamespaces = false)
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
ns = new XmlSerializerNamespaces();
using (var textWriter = new StringWriter())
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " };
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
new XmlSerializer(obj.GetType()).Serialize(xmlWriter, obj, ns);
return textWriter.ToString();
public static class ConsoleAndDebug
public static void WriteLine(object s)