using System.Collections.Generic;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO.Compression;
public class XmlSerializerFactory : XmlOrderFreeSerializerFactory
static readonly XmlSerializerFactory instance;
private XmlSerializerFactory()
: base(new[] { typeof(Type2), typeof(Type1), typeof(TestClass), typeof(Type3) })
static XmlSerializerFactory()
instance = new XmlSerializerFactory();
public static XmlSerializerFactory Instance { get { return instance; } }
public abstract class XmlOrderFreeSerializerFactory
readonly XmlAttributeOverrides overrides;
readonly object locker = new object();
readonly Dictionary<Type, XmlSerializer> serializers = new Dictionary<Type, XmlSerializer>();
static void AddOverrideAttributes(Type type, XmlAttributeOverrides overrides)
if (type == null || type == typeof(object) || type.IsPrimitive || type == typeof(string))
var mask = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;
foreach (var member in type.GetProperties(mask).Cast<MemberInfo>().Union(type.GetFields(mask)))
XmlAttributes overrideAttr = null;
foreach (var attr in member.GetCustomAttributes<XmlElementAttribute>())
overrideAttr = overrideAttr ?? new XmlAttributes();
overrideAttr.XmlElements.Add(new XmlElementAttribute { DataType = attr.DataType, ElementName = attr.ElementName, Form = attr.Form, IsNullable = attr.IsNullable, Namespace = attr.Namespace, Type = attr.Type });
foreach (var attr in member.GetCustomAttributes<XmlArrayAttribute>())
overrideAttr = overrideAttr ?? new XmlAttributes();
overrideAttr.XmlArray = new XmlArrayAttribute { ElementName = attr.ElementName, Form = attr.Form, IsNullable = attr.IsNullable, Namespace = attr.Namespace };
foreach (var attr in member.GetCustomAttributes<XmlArrayItemAttribute>())
overrideAttr = overrideAttr ?? new XmlAttributes();
overrideAttr.XmlArrayItems.Add(attr);
foreach (var attr in member.GetCustomAttributes<XmlAnyElementAttribute>())
overrideAttr = overrideAttr ?? new XmlAttributes();
overrideAttr.XmlAnyElements.Add(new XmlAnyElementAttribute { Name = attr.Name, Namespace = attr.Namespace });
if (overrideAttr != null)
overrides.Add(type, member.Name, overrideAttr);
protected XmlOrderFreeSerializerFactory(IEnumerable<Type> types)
overrides = new XmlAttributeOverrides();
foreach (var type in types.SelectMany(t => t.BaseTypesAndSelf()).Distinct())
AddOverrideAttributes(type, overrides);
public XmlSerializer GetSerializer(Type type)
throw new ArgumentNullException("type");
XmlSerializer serializer;
if (!serializers.TryGetValue(type, out serializer))
serializers[type] = serializer = new XmlSerializer(type, overrides);
public static class TypeExtensions
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
public string BaseProperty1 { get; set; }
public string[] BaseArray1 { get; set; }
[XmlArrayItem("InnerStringName")]
public string[] BaseArray2 { get; set; }
[XmlElement(Order = 4, ElementName = "BaseArray3_IntValue", Type = typeof(int))]
[XmlElement(Order = 4, ElementName = "BaseArray3_StringValue", Type = typeof(string))]
public object[] BaseArray3 { get; set; }
public object DoNotSerialize { get; set; } = new object();
public string Text { get; set; } = "hello there";
[XmlAnyElement(Order = 5)]
public XElement [] XmlAnyElementElements { get; set; } = new [] { new XElement("ChildAnyElement") };
public class Type2 : Type1
[XmlElement("derivedPropertyName", Order = 21, Namespace= "foo bar namespace")]
public string DerivedProperty { get; set; }
public class Type3 : Type1
[XmlElement("derivedPropertyName2", Order = 21, Namespace = "foo bar namespace")]
public string DerivedProperty2 { get; set; }
public static class StringExtensions
public static string StripAfter(this string s, string postfix)
var index = s.IndexOf(postfix);
return s.Substring(0, index);
public static void Test()
TestFactorySerialization();
static void TestRoundTrip()
var factory = XmlSerializerFactory.Instance;
var test = new Type2 { BaseArray1 = new[] { "a", "b" }, BaseArray2 = new[] { "1", "2" }, BaseProperty1 = "base property 1", DerivedProperty = "derived property", BaseArray3 = new object[] { 101, "hello" } };
var xml = test.SerializeToXElement();
Console.WriteLine("\n{0} serialized with the default serializer:", test);
var xml2 = new XElement(xml.Name, xml.Attributes(), xml.Elements().GroupBy(e => e.Name.LocalName.StripAfter("_")).Reverse().Select(g => g), xml.Nodes().Where(n => !(n is XElement)));
Console.WriteLine("\n{0} serialized with the default serializer and then its elements reordered afterwards:", test);
var test2 = xml2.Deserialize<Type2>(factory.GetSerializer(typeof(Type2)));
var xml3 = test2.SerializeToXElement();
Console.WriteLine("\nRe-serialized {0} with the default serializer:", test);
Assert.AreEqual(xml.ToString(), xml3.ToString());
static void TestFactorySerialization()
var factory = XmlSerializerFactory.Instance;
var test = new Type2 { BaseArray1 = new[] { "a", "b" }, BaseArray2 = new[] { "1", "2" }, BaseProperty1 = "base property 1", DerivedProperty = "derived property", BaseArray3 = new object[] { 101, "hello" } };
var xmlSerializedWithFactory = test.SerializeToXElement(factory.GetSerializer(typeof(Type2)), true);
var xmlSerializedWithFactoryString = xmlSerializedWithFactory.ToString();
Console.WriteLine("\n{0} serialized with the factory-generated serializer:", test);
Console.WriteLine(xmlSerializedWithFactoryString);
Assert.That(!xmlSerializedWithFactoryString.Contains(nameof(Type1.DoNotSerialize)));
Assert.That(!xmlSerializedWithFactoryString.Contains(nameof(Type1.Text)), "!xmlSerializedWithFactoryString.Contains(nameof(Type1.Text))");
Assert.That(xmlSerializedWithFactoryString.Contains(test.Text), "xmlSerializedWithFactoryString.Contains(test.Text)");
Assert.That(!xmlSerializedWithFactoryString.Contains(nameof(Type1.XmlAnyElementElements)), "!xmlSerializedWithFactoryString.Contains(nameof(Type1.XmlAnyElementElements))");
Assert.That(xmlSerializedWithFactoryString.Contains(test.XmlAnyElementElements[0].Name.LocalName), "xmlSerializedWithFactoryString.Contains(test.XmlAnyElementElements[0].Name.LocalName)");
public static class XObjectExtensions
public static T Deserialize<T>(this XContainer element)
return element.Deserialize<T>(null);
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer)
using (var reader = element.CreateReader())
object result = (serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
public static object Deserialize(this XContainer element, Type type)
return element.Deserialize(type, null);
public static object Deserialize(this XContainer element, Type type, XmlSerializer serializer)
using (var reader = element.CreateReader())
return (serializer ?? new XmlSerializer(type)).Deserialize(reader);
public static XElement SerializeToXElement<T>(this IDictionary<string, T> collection, string collectionName)
return new XElement(collectionName, collection.Select(x => new XElement("Item", new XAttribute("Object", x.Key), x.Value.SerializeToXElement().Elements())));
public static XmlSerializerNamespaces NoStandardXmlNamespaces()
var ns = new XmlSerializerNamespaces();
public static XElement SerializeToXElement<T>(this T obj)
return obj.SerializeToXElement(null, NoStandardXmlNamespaces());
public static XElement SerializeToXElement<T>(this T obj, XmlSerializerNamespaces ns)
return obj.SerializeToXElement(null, ns);
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, bool omitStandardNamespaces)
return obj.SerializeToXElement(serializer, (omitStandardNamespaces ? NoStandardXmlNamespaces() : null));
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, XmlSerializerNamespaces ns)
var doc = new XDocument();
using (var writer = doc.CreateWriter())
(serializer ?? new XmlSerializer(obj.GetType())).Serialize(writer, obj, ns);
public static XDocument SerializeToXDocument<T>(this T obj)
return obj.SerializeToXDocument(null, true);
public static XDocument SerializeToXDocument<T>(this T obj, XmlSerializer serializer, bool omitStandardNamespaces)
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
(ns = new XmlSerializerNamespaces()).Add("", "");
return SerializeToXDocument(obj, serializer, ns);
public static XDocument SerializeToXDocument<T>(this T obj, XmlSerializer serializer, XmlSerializerNamespaces ns)
var doc = new XDocument();
using (var writer = doc.CreateWriter())
(serializer ?? new XmlSerializer(obj.GetType())).Serialize(writer, obj, ns);
public static class XmlSerializationHelper
public static T LoadFromXml<T>(this string xmlString, XmlSerializer serial = null)
serial = serial ?? new XmlSerializer(typeof(T));
using (var reader = new StringReader(xmlString))
return (T)serial.Deserialize(reader);
public static string GetXml<T>(this T obj, bool omitStandardNamespaces)
return obj.GetXml(null, omitStandardNamespaces);
public static string GetXml<T>(this T obj, XmlSerializer serializer = null, bool omitStandardNamespaces = false)
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
ns = new XmlSerializerNamespaces();
using (var textWriter = new StringWriter())
var settings = new XmlWriterSettings() { Indent = true };
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
(serializer ?? new XmlSerializer(obj.GetType())).Serialize(xmlWriter, obj, ns);
return textWriter.ToString();
public static void Main()
Console.WriteLine("Environment version: {0} ({1}, {2})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version, Environment.OSVersion);
Console.WriteLine("Failed with unhandled exception: ");