using System.Collections.Generic;
using System.Xml.Serialization;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Globalization;
using System.ComponentModel.DataAnnotations;
using System.Collections;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
[System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)]
public class XmlUnknownElementEventHandlerAttribute : System.Attribute
public static partial class XmlSerializationHelper
public static T LoadFromXml<T>(this string xmlString, XmlSerializer serial = null)
serial = serial ?? new XmlSerializer(typeof(T));
serial.UnknownElement += UnknownXmlElementEventHandler;
using (StringReader reader = new StringReader(xmlString))
return (T)serial.Deserialize(reader);
public static void UnknownXmlElementEventHandler(object sender, XmlElementEventArgs e)
var obj = e.ObjectBeingDeserialized;
foreach (var method in obj.GetType().BaseTypesAndSelf()
.SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
.Where(m => Attribute.IsDefined(m, typeof(XmlUnknownElementEventHandlerAttribute))))
method.Invoke(obj, BindingFlags.Public | BindingFlags.NonPublic, null, new object[] { sender, e }, null);
public static class TypeExtensions
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
public partial class MyClass
public string MyValue { get; set; }
public partial class MyClass
[XmlUnknownElementEventHandler]
public void HandleOldElement(object sender, XmlElementEventArgs e)
if (e.Element.Name == "OldValue")
Console.WriteLine("{0}: processed property {1} with value {2}", this, e.Element.Name, e.Element.OuterXml);
MyValue = "Old value was: " + e.Element.InnerText;
public class DerivedClass : MyClass
public string DerivedValue { get; set; }
[XmlUnknownElementEventHandler]
public void HandleOldDerivedElement(object sender, XmlElementEventArgs e)
if (e.Element.Name == "OldDerivedProperty")
Console.WriteLine("{0}: processed property {1} with value {2}", this, e.Element.Name, e.Element.OuterXml);
DerivedValue = "Old value was: " + e.Element.InnerText;
public static void Test()
Test<MyClass>(GetOldMyClassXml(), GetNewExpectedMyClassXml());
Test<DerivedClass>(GetOldDerivedXml(), GetNewExpectedDerivedXml());
Console.WriteLine("\nAll tests passed.");
private static void Test<T>(string xmlString, string expectedXml)
Console.WriteLine("\nOld XML: ");
Console.WriteLine(xmlString);
var model = xmlString.LoadFromXml<T>();
var xml2 = model.GetXml(true);
Console.WriteLine("Deserialized and re-serialized {0}:", model);
Assert.IsTrue(XNode.DeepEquals(XElement.Parse(expectedXml), XElement.Parse(xml2)));
Console.WriteLine("Re-serialized XML is as expected.");
static string GetNewExpectedDerivedXml()
var xml = @"<DerivedClass>
<MyValue>Old value was: Hello</MyValue>
<DerivedValue>Old value was: Hello Again</DerivedValue>
static string GetOldDerivedXml()
var xml = @"<DerivedClass><OldValue>Hello</OldValue><OldDerivedProperty>Hello Again</OldDerivedProperty></DerivedClass>";
static string GetOldMyClassXml()
var xml = @"<MyClass><OldValue>Hello</OldValue></MyClass>";
static string GetNewExpectedMyClassXml()
<MyValue>Old value was: Hello</MyValue>
public class AssertionFailedException : System.Exception
public AssertionFailedException() : base() { }
public AssertionFailedException(string s) : base(s) { }
public static class Assert
public static void IsTrue(bool value)
public static void IsTrue(bool value, string message)
throw new AssertionFailedException(message ?? "failed");
public static partial class XmlSerializationHelper
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: " + Environment.Version);