This is the third part of the SQLxD series. For your convenience you can find other parts in the table of contents in Part 1 – XML Transformation

We want to represent nodes in the following way:

using System;
using System.Collections.Generic;
using System.Linq;

namespace Model
{
    public class Node : IEquatable
    {
        public IEnumerable InnerNodes;

        public Node()
        {
            Value = null;
            InnerNodes = new List();
            Guid = Guid.NewGuid();
        }

        public NodeType Type { get; set; }
        public string Value { get; set; }
        public string Name { get; set; }
        public Node Parent { get; set; }
        public Guid Guid { get; private set; }

        public bool Equals(Node other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return InnerNodes.SequenceEqual(other.InnerNodes) && Type == other.Type && string.Equals(Value, other.Value) &&
                   string.Equals(Name, other.Name);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((Node)obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int hashCode = (InnerNodes != null ? InnerNodes.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ (int)Type;
                hashCode = (hashCode * 397) ^ (Value != null ? Value.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ (Parent != null ? Parent.GetHashCode() : 0);
                return hashCode;
            }
        }
    }
}

Node has its type, name, pointer to parent, and identifier. Type is one of these:

namespace Model
{
   public enum NodeType
   {
      Invalid,
      Element,
      Attribute,
      Text
   }
}

Now we can parse the document:

using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Model;

namespace Parser
{
    public static class XmlDocumentParser
    {
        public static Node Parse(string xml)
        {
            XDocument document = XDocument.Parse(xml);
            return ParseDocument(document);
        }

        private static Node ParseDocument(XDocument document)
        {
            XElement rootNode = document.Root;
            if (rootNode == null)
            {
                return null;
            }

            Node result = ParseElement(rootNode);
            FixParents(result);

            return result;
        }

        private static Node ParseElement(XElement rootNode)
        {
            var result = new Node
            {
                Name = rootNode.Name.LocalName,
                Value = ParseNodeTextValue(rootNode),
                InnerNodes = ParseInnerNodes(rootNode),
                Type = NodeType.Element,
            };

            return result;
        }

        private static string ParseNodeTextValue(XElement element)
        {
            return GetOneLevelValue(element);
        }

        private static string GetOneLevelValue(XElement element)
        {
            return string.Concat(element.Nodes().OfType().Select(t => t.Value));
        }

        private static IEnumerable ParseInnerNodes(XElement element)
        {
            return
                new List { ParseNodeTextValueAsNode(element) }.Concat(ParseAttributes(element))
                    .Concat(ParseChilds(element)).ToArray();
        }

        private static Node ParseNodeTextValueAsNode(XElement element)
        {
            return new Node
            {
                Name = "#",
                Value = ParseNodeTextValue(element),
                Type = NodeType.Text,
            };
        }

        private static IEnumerable ParseAttributes(XElement element)
        {
            return element.Attributes().Select(ParseSingleAttribute);
        }

        private static Node ParseSingleAttribute(XAttribute attribute)
        {
            return new Node
            {
                Name = attribute.Name.LocalName,
                Value = attribute.Value,
                Type = NodeType.Attribute,
                InnerNodes = new List
                {
                    new Node
                    {
                        Name = "#",
                        Value = attribute.Value,
                        Type = NodeType.Text,
                    }
                }
            };
        }

        private static IEnumerable ParseChilds(XElement element)
        {
            return element.Elements().Select(ParseElement);
        }


        private static void FixParents(Node parent)
        {
            foreach (Node child in parent.InnerNodes)
            {
                child.Parent = parent;
                FixParents(child);
            }
        }
    }
}

We simply traverse whole document and create nodes for every XML node.