Search code examples
c#xmldynamicdeserializationxmlserializer

Deserialize XML To Object using Dynamic


Is it possible Deserialize unknown XML to object like below?

 var xml = @"<Students><Student><Name>Arul</Name><Mark>90</Mark></Student></Students>";

 var serializer = new XmlSerializer(typeof(DynamicObject));

 dynamic students = serializer.Deserialize(new XmlTextReader(new StringReader(xml)));

Solution

  • You may want to try this.

    string xml = @"<Students>
                    <Student ID=""100"">
                        <Name>Arul</Name>
                        <Mark>90</Mark>
                    </Student>
                    <Student>
                        <Name>Arul2</Name>
                        <Mark>80</Mark>
                    </Student>
                </Students>";
    
    dynamic students = DynamicXml.Parse(xml);
    
    var id = students.Student[0].ID;
    var name1 = students.Student[1].Name;
    
    foreach(var std in students.Student)
    {
        Console.WriteLine(std.Mark);
    }
    

    public class DynamicXml : DynamicObject
    {
        XElement _root;
        private DynamicXml(XElement root)
        {
            _root = root;
        }
    
        public static DynamicXml Parse(string xmlString)
        {
            return new DynamicXml(XDocument.Parse(xmlString).Root);
        }
    
        public static DynamicXml Load(string filename)
        {
            return new DynamicXml(XDocument.Load(filename).Root);
        }
    
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = null;
    
            var att = _root.Attribute(binder.Name);
            if (att != null)
            {
                result = att.Value;
                return true;
            }
    
            var nodes = _root.Elements(binder.Name);
            if (nodes.Count() > 1)
            {
                result = nodes.Select(n => n.HasElements ? (object)new DynamicXml(n) : n.Value).ToList();
                return true;
            }
    
            var node = _root.Element(binder.Name);
            if (node != null)
            {
                result = node.HasElements || node.HasAttributes ? (object)new DynamicXml(node) : node.Value;
                return true;
            }
    
            return true;
        }
    }
    

    --EDIT--

    To make it work with xml namespaces, I added RemoveNamespaces method.

    public class DynamicXml : DynamicObject
    {
        XElement _root;
        private DynamicXml(XElement root)
        {
            _root = root;
        }
    
        public static DynamicXml Parse(string xmlString)
        {
            return new DynamicXml(RemoveNamespaces(XDocument.Parse(xmlString).Root));
        }
    
        public static DynamicXml Load(string filename)
        {
            return new DynamicXml(RemoveNamespaces(XDocument.Load(filename).Root));
        }
    
        private static XElement RemoveNamespaces(XElement xElem)
        {
            var attrs = xElem.Attributes()
                        .Where(a => !a.IsNamespaceDeclaration)
                        .Select(a => new XAttribute(a.Name.LocalName, a.Value))
                        .ToList();
    
            if (!xElem.HasElements)
            {
                XElement xElement = new XElement(xElem.Name.LocalName, attrs);
                xElement.Value = xElem.Value;
                return xElement;
            }
    
            var newXElem = new XElement(xElem.Name.LocalName, xElem.Elements().Select(e => RemoveNamespaces(e)));
            newXElem.Add(attrs);
            return newXElem;
        }
    
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = null;
    
            var att = _root.Attribute(binder.Name);
            if (att != null)
            {
                result = att.Value;
                return true;
            }
    
            var nodes = _root.Elements(binder.Name);
            if (nodes.Count() > 1)
            {
                result = nodes.Select(n => n.HasElements ? (object)new DynamicXml(n) : n.Value).ToList();
                return true;
            }
    
            var node = _root.Element(binder.Name);
            if (node != null)
            {
                result = node.HasElements || node.HasAttributes ? (object)new DynamicXml(node) : node.Value;
                return true;
            }
    
            return true;
        }
    }