using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
public static class XmlSerializationExtensions
public static void ReadIXmlSerializable(XmlReader reader, Func<XmlReader, bool> handleXmlAttribute, Func<XmlReader, bool> handleXmlElement, Func<XmlReader, bool> handleXmlText)
if (reader.NodeType != XmlNodeType.Element)
throw new XmlException(string.Format("Invalid NodeType {0}", reader.NodeType));
if (reader.HasAttributes)
for (int i = 0; i < reader.AttributeCount; i++)
reader.MoveToAttribute(i);
handleXmlAttribute(reader);
if (reader.IsEmptyElement)
reader.ReadStartElement();
while (reader.NodeType != XmlNodeType.EndElement)
if (reader.NodeType == XmlNodeType.Element)
using (var subReader = reader.ReadSubtree())
subReader.MoveToContent();
handleXmlElement(subReader);
else if (reader.NodeType == XmlNodeType.Text || reader.NodeType == XmlNodeType.CDATA)
var type = reader.NodeType;
if (reader.NodeType != type)
throw new XmlException(string.Format("handleXmlText incorrectly advanced the reader to a new node {0}", reader.NodeType));
public static void WriteIXmlSerializable(XmlWriter writer, Action<XmlWriter> writeAttributes, Action<XmlWriter> writeNodes)
public const string ExampleNamespace = "example";
[XmlRoot(Namespace = Constants.ExampleNamespace)]
public Groups Groups { get; set; }
public class Groups : EntityCollection<Group>
public class EntityCollection<T> : IXmlSerializable, IList<T> where T : Entity
private List<T> childEntityField;
public EntityCollection()
childEntityField = new List<T>();
#region IXmlSerializable Implementation
public XmlSchema GetSchema() { return null; }
protected internal virtual bool HandleXmlAttribute(XmlReader reader) { return false; }
protected internal virtual void WriteAttributes(XmlWriter writer) { }
protected internal virtual bool HandleXmlElement(XmlReader reader)
var serializer = new XmlSerializer(typeof(T), Constants.ExampleNamespace);
if (serializer.CanDeserialize(reader))
T item = (T)serializer.Deserialize(reader);
protected internal virtual void WriteNodes(XmlWriter writer)
var serializer = new XmlSerializer(typeof(T), Constants.ExampleNamespace);
foreach (var item in this)
serializer.Serialize(writer, item);
public void ReadXml(XmlReader reader)
XmlSerializationExtensions.ReadIXmlSerializable(reader, r => HandleXmlAttribute(r), r => HandleXmlElement(r), r => false);
public void WriteXml(XmlWriter writer)
XmlSerializationExtensions.WriteIXmlSerializable(writer, w => WriteAttributes(w), w => WriteNodes(w));
#region IList Implementation
public IEnumerator<T> GetEnumerator()
return childEntityField.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
return ((IEnumerable)childEntityField).GetEnumerator();
childEntityField.Add(item);
childEntityField.Clear();
public bool Contains(T item)
return childEntityField.Contains(item);
public void CopyTo(T[] array, int arrayIndex)
childEntityField.CopyTo(array, arrayIndex);
public bool Remove(T item)
return childEntityField.Remove(item);
public int Count { get { return childEntityField.Count; } }
public bool IsReadOnly { get { return ((ICollection<T>)childEntityField).IsReadOnly; } }
public int IndexOf(T item)
return childEntityField.IndexOf(item);
public void Insert(int index, T item)
childEntityField.Insert(index, item);
public void RemoveAt(int index)
childEntityField.RemoveAt(index);
get { return childEntityField[index]; }
set { childEntityField[index] = value; }
public class Group : Entity, IXmlSerializable
private EntityCollection<DerivedEntity> entityCollection;
this.entityCollection = new EntityCollection<DerivedEntity>();
#region IXmlSerializable Implementation
public XmlSchema GetSchema()
protected override bool HandleXmlElement(XmlReader reader)
if (base.HandleXmlElement(reader))
return entityCollection.HandleXmlElement(reader);
protected override void WriteNodes(XmlWriter writer)
entityCollection.WriteNodes(writer);
protected override bool HandleXmlAttribute(XmlReader reader)
if (base.HandleXmlAttribute(reader))
if (entityCollection.HandleXmlAttribute(reader))
protected override void WriteAttributes(XmlWriter writer)
base.WriteAttributes(writer);
entityCollection.WriteAttributes(writer);
public void ReadXml(XmlReader reader)
XmlSerializationExtensions.ReadIXmlSerializable(reader, r => HandleXmlAttribute(r), r => HandleXmlElement(r), r => false);
public void WriteXml(XmlWriter writer)
XmlSerializationExtensions.WriteIXmlSerializable(writer, w => WriteAttributes(w), w => WriteNodes(w));
public class DerivedEntity : Entity
public string Parameter { get; set; }
[System.Xml.Serialization.XmlIncludeAttribute(typeof(DerivedEntity))]
public abstract class Entity
public string Description { get; set; }
public string Id { get; set; }
public string Name { get; set; }
protected virtual void WriteAttributes(XmlWriter writer)
writer.WriteAttributeString("Id", Id);
writer.WriteAttributeString("Name", Name);
protected virtual bool HandleXmlAttribute(XmlReader reader)
if (reader.LocalName == "Id")
else if (reader.LocalName == "Name")
protected virtual void WriteNodes(XmlWriter writer)
writer.WriteElementString("Description", Description);
protected virtual bool HandleXmlElement(XmlReader reader)
if (reader.LocalName == "Description")
Description = reader.ReadElementContentAsString();
public class StackOverflowExample
public void InvalidElementInGroupTest()
foreach (var xml in GetXml())
using (var schema = XmlReader.Create(new StringReader(GetXsd())))
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
Console.WriteLine("\nTesting deserialization...");
var settings = new XmlReaderSettings();
settings.Schemas.Add(null, schema);
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += (o, e) =>
Console.WriteLine("Validation Event: " + e.Message);
XmlSerializer xmlDeserializer = new XmlSerializer(typeof(Example), "example");
using (var xmlReader = XmlReader.Create(stream, settings))
var deserializedObject = (Example)xmlDeserializer.Deserialize(xmlReader);
var reserializedXml = deserializedObject.GetXml();
Console.WriteLine("Deserialized successfully with {0} validation events. Re-serialized XML:", errorCount);
Console.WriteLine(reserializedXml);
var inXmlCleaned = XElement.Parse(xml);
inXmlCleaned.Descendants().Where(e => e.Name.LocalName == "UnknownElement").Remove();
XmlAssert.AreEqual(inXmlCleaned, XElement.Parse(reserializedXml));
Assert.IsTrue(errorCount == 1);
static IEnumerable<string> GetXml()
var xml = @"<?xml version=""1.0""?>
<Example xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""example"">
<!-- A comment to mess things up. -->
<Group Name=""abc"" Id=""123"">
<DerivedEntity Id=""123"" Name=""xyz"" Parameter=""ijk"">
<Description>def</Description>
<DerivedEntity Id=""234"" Name=""bob"" Parameter=""12""/>
<Group Name=""def"" Id=""124"">
<Description>This is a description.</Description>
XElement.Parse(xml).ToString(SaveOptions.DisableFormatting),
return @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<xs:schema xmlns:local=""example""
attributeFormDefault=""unqualified""
elementFormDefault=""qualified""
targetNamespace=""example""
xmlns:xs=""http://www.w3.org/2001/XMLSchema"">
<!-- Attribute Groups -->
<xs:attributeGroup name=""Identifiers"">
<xs:attribute name=""Id""
<xs:attribute name=""Name""
<xs:complexType abstract=""true""
<xs:element name=""Description""
<xs:attributeGroup ref=""local:Identifiers"" />
<xs:complexType name=""DerivedEntity"">
<xs:extension base=""local:Entity"">
<xs:attribute name=""Parameter""
<xs:complexType name=""Groups"">
<xs:element name=""Group"" type=""local:Group"" minOccurs=""0"" maxOccurs=""unbounded""/>
<xs:complexType name=""Group"">
<xs:extension base=""local:Entity"">
<xs:element name=""DerivedEntity""
type=""local:DerivedEntity""
maxOccurs=""unbounded"" />
<!-- Main Schema Definition -->
<xs:element name=""Example"">
<xs:element name=""Groups""
public static void Test()
new StackOverflowExample().InvalidElementInGroupTest();
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 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];