Question37209142 XML adding-the-parent-id-to-serialization-as-object-class

CSharp · Console

/Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.XPath;
using System.ComponentModel;
using System.Collections.ObjectModel;

					
    [System.SerializableAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
    public partial class Steps
    {
        readonly ChildCollection<Step> steps;

        public Steps()
        {
            this.steps = new ChildCollection<Step>();
            this.steps.ChildAdded += (s, e) =>
            {
                if (e.Item != null)
                    e.Item.ParentId = null;
            };
        }

        [System.Xml.Serialization.XmlElementAttribute("Step")]
        public Collection<Step> StepList { get { return steps; } }
    }

    [System.SerializableAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
    public partial class Step
    {
        readonly ChildCollection<Step> steps;

        public Step()
        {
            this.steps = new ChildCollection<Step>();
            this.steps.ChildAdded += (s, e) =>
            {
                if (e.Item != null)
                    e.Item.ParentId = this.Id;
            };
        }

        [System.Xml.Serialization.XmlElementAttribute("Step")]
        public Collection<Step> StepList { get { return steps; } }

        [System.Xml.Serialization.XmlAttributeAttribute("Name")]
        public string Name { get; set; }
        [System.Xml.Serialization.XmlAttributeAttribute("id")]
        public string Id { get; set; }
        [System.Xml.Serialization.XmlAttributeAttribute("ParentID")]
        public string ParentId { get; set; }
    }

    public class ChildCollectionEventArgs<TChild> : EventArgs
    {
        public readonly TChild Item;

        public ChildCollectionEventArgs(TChild item)
        {
            this.Item = item;
        }
    }

    public class ChildCollection<TChild> : Collection<TChild>
    {
        public event EventHandler<ChildCollectionEventArgs<TChild>> ChildAdded;

        public event EventHandler<ChildCollectionEventArgs<TChild>> ChildRemoved;

        void OnRemoved(TChild item)
        {
            var removed = ChildRemoved;
            if (removed != null)
                removed(this, new ChildCollectionEventArgs<TChild>(item));
        }

        void OnAdded(TChild item)
        {
            var added = ChildAdded;
            if (added != null)
                added(this, new ChildCollectionEventArgs<TChild>(item));
        }

        public ChildCollection() : base() { }

        protected override void ClearItems()
        {
            foreach (var item in this)
                OnRemoved(item);
            base.ClearItems();
        }

        protected override void InsertItem(int index, TChild item)
        {
            OnAdded(item);
            base.InsertItem(index, item);
        }

        protected override void RemoveItem(int index)
        {
            if (index >= 0 && index < Count)
            {
                OnRemoved(this[index]);
            }
            base.RemoveItem(index);
        }

        protected override void SetItem(int index, TChild item)
        {
            OnAdded(item);
            base.SetItem(index, item);
        }
    }

    public static class StepExtensions
    {
        public static IEnumerable<Step> TraverseSteps(this Steps root)
        {
            if (root == null)
                throw new ArgumentNullException();
            return RecursiveEnumerableExtensions.Traverse(root.StepList, s => s.StepList);
        }

        public static IEnumerable<Step> TraverseSteps(this Step root)
        {
            if (root == null)
                throw new ArgumentNullException();
            return RecursiveEnumerableExtensions.Traverse(root, s => s.StepList);
        }

        public static bool TryAdd(this Steps root, Step step, string parentId)
        {
            foreach (var item in root.TraverseSteps())
                if (item != null && item.Id == parentId)
                {
                    item.StepList.Add(step);
                    return true;
                }
            return false;
        }

        public static void Add(this Steps root, Step step, string parentId)
        {
            if (!root.TryAdd(step, parentId))
                throw new InvalidOperationException(string.Format("Parent {0} not found", parentId));
        }
    }

    public static class RecursiveEnumerableExtensions
    {
        // sort of adapted from https://stackoverflow.com/questions/10253161/efficient-graph-traversal-with-linq-eliminating-recursion        

        public static IEnumerable<T> Traverse<T>(
            T root,
            Func<T, IEnumerable<T>> children)
        {
            yield return root;

            var stack = new Stack<IEnumerator<T>>();
            try
            {
                stack.Push(children(root).GetEnumerator());

                while (stack.Count != 0)
                {
                    var enumerator = stack.Peek();
                    if (!enumerator.MoveNext())
                    {
                        stack.Pop();
                        enumerator.Dispose();
                    }
                    else
                    {
                        yield return enumerator.Current;
                        stack.Push(children(enumerator.Current).GetEnumerator());
                    }
                }
            }
            finally
            {
                foreach (var enumerator in stack)
                    enumerator.Dispose();
            }
        }

        public static IEnumerable<T> Traverse<T>(
            IEnumerable<T> roots,
            Func<T, IEnumerable<T>> children)
        {
            return from root in roots
                   from item in Traverse(root, children)
                   select item;
        }
    }

    public static class TestClass
    {
        static string GetXml()
        {
            var xml = @"<Steps >
  <Step id =""1"" Name=""S1"">
    <Step id =""2"" Name=""S11"">
      <Step id =""3"" Name=""S111"" />
      <Step id =""4"" Name=""S112"" />
        <Step id =""5"" Name=""S1121"" />
    </Step >
    <Step id =""6"" Name=""S12"" />
  </Step >
</Steps >";
            return xml;
        }
		
		static void ValidateParentIds(Steps steps)
		{
            foreach (var item in steps.TraverseSteps())
            {
				foreach (var subItem in item.StepList)
				{
					if (subItem != null && subItem.ParentId != item.Id)
						throw new InvalidOperationException(string.Format("Child {0} of parent {1} has wrong parent id {2}", subItem.Id, item.Id, subItem.ParentId));
				}
            }
		}
		

        public static void Test()
        {
            var steps = GetXml().LoadFromXML<Steps>();

            var xml2 = steps.GetXml();

			Console.WriteLine("Re-serialized XML with ParentId values set: ");
            Console.WriteLine(xml2);
			Console.WriteLine("");

			Console.WriteLine("List of all step IDs in pre-order traversal: ");
            foreach (var item in steps.TraverseSteps())
            {
                Console.WriteLine(item.Id);
				
            }
			Console.WriteLine("");

			ValidateParentIds(steps);
			
            steps.Add(new Step { Id = "4C", Name = "S112C" }, "4");

            var xml3 = steps.GetXml();

			Console.WriteLine("Re-serialized XML with step '4C' added to step '4': ");
            Console.WriteLine(xml3);

			ValidateParentIds(steps);
        }
    }

    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();
                ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
            }			
            using (var textWriter = new StringWriter())
            {
                var settings = new XmlWriterSettings() { Indent = true, IndentChars = "    " }; // For cosmetic purposes.
                using (var xmlWriter = XmlWriter.Create(textWriter, settings))
                    new XmlSerializer(obj.GetType()).Serialize(xmlWriter, obj, ns);
                return textWriter.ToString();
            }
        }
    }

	public class Program
	{
		public static void Main()
		{
			TestClass.Test();
		}
	}
;