using System.Collections.Generic;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.IO.Compression;
public interface IHasThings
List<SomeThing> ChildThings { get; set; }
public class Things : IHasThings
[XmlElement(typeof(ThingOne), ElementName = "ThingOne")]
[XmlElement(typeof(ThingTwo), ElementName = "ThingTwo")]
public List<SomeThing> ChildThings { get; set; } = new List<SomeThing>();
public abstract class SomeThing : IHasThings
public int ItemNumber { get; set; }
public string Name {get; set;}
[XmlElement(typeof(ThingOne), ElementName = "ThingOne")]
[XmlElement(typeof(ThingTwo), ElementName = "ThingTwo")]
public List<SomeThing> ChildThings {get; set;} = new List<SomeThing>();
public abstract void InspectChildren();
public sealed class ThingOne : SomeThing
public override void InspectChildren()
Console.WriteLine("Called {0}.{1}", GetType().Name, nameof(InspectChildren));
public sealed class ThingTwo : SomeThing
public override void InspectChildren()
Console.WriteLine("Called {0}.{1}", GetType().Name, nameof(InspectChildren));
public static void Test()
static void TestDeserialize()
<ThingOne Name=""FirstThing"">
<ThingOne Name=""FirstChildThing"" />
<ThingTwo Name=""SecondChildThing""/>
<ThingTwo Name=""SecondThing"">
<ThingOne Name=""ThirdChildThing"" />
<ThingTwo Name=""FourthChildThing"" />
Console.WriteLine("Testing deserialization. Input XML:");
var things = xml.LoadFromXml<Things>();
var xml2 = things.GetXml(omitStandardNamespaces: true);
Assert.IsTrue(XNode.DeepEquals(XElement.Parse(xml), XElement.Parse(xml2)));
Console.WriteLine("Re-serialized {0}:", things);
static void TestSerialize()
new ThingOne { Name = "FirstChildThing" }, new ThingTwo { Name = "SecondChildThing" },
new ThingOne { Name = "ThirdChildThing" }, new ThingTwo { Name = "FourthChildThing" },
var xml = things.GetXml(omitStandardNamespaces: true);
Console.WriteLine("\nTesting serialization for {0}:", things);
var things2 = xml.LoadFromXml<Things>();
var xml2 = things2.GetXml(omitStandardNamespaces: true);
Assert.AreEqual(xml, xml2);
Console.WriteLine("Round-trip successful.");
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, 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 string GetOuterXml(this XmlNode node, bool indent = true)
using (var textWriter = new StringWriter())
var settings = new XmlWriterSettings
OmitXmlDeclaration = true,
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
return textWriter.ToString();
public static class XmlAssert
public static void AreEqual(
Assert.IsTrue(XNode.DeepEquals(Normalize(expected), Normalize(actual)));
private static XElement Normalize(XElement element)
.OrderBy(a => a.Name.ToString()),
.OrderBy(a => a.Name.ToString())
.Select(e => Normalize(e)));
.OrderBy(a => a.Name.ToString()));
return new XElement(element.Name, element.Attributes()
.OrderBy(a => a.Name.ToString()), element.Value);
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];