Click here to Skip to main content
Click here to Skip to main content

Converting XML to an dynamic object using ExpandoObject

, 14 Dec 2011
Rate this:
Please Sign up or sign in to vote.

I started looking on the Internet to find an straightforward way of loading in an xml file and converting it to an object on the fly. my goal was to dynamically load setting files in xml format for a web project I'm working on.

As the definition of the xml schema is very different per setup I tough of using the dynamic keyword to load an xml document and access it as a simple object. After some browsing I found some examples that used the ExpandoObject. unfortunately they all either write about converting an object to xml or how to use XML-to-LINQ to map a specific dynamic class/object.

 

So I ended up writing a small recursive method that loads an xml document and converts it to a simple object.

 
The code:
private static dynamic _getExpandoFromXml(String file, XElement node = null)
{
    if (String.IsNullOrWhiteSpace(file) && node == null) return null;
    // If a file is not empty then load the xml and overwrite node with the
    // root element of the loaded document
    if (!String.IsNullOrWhiteSpace(file))
    {
        var doc = XDocument.Load(file);
        node = doc.Root;
    }
    dynamic result = new ExpandoObject();
 

    foreach (var gn in node.Elements())
    {
        // The code determines if it is a container node based on the child
        // elements with the same name. If there is only one child element,
        // but it should still be treated as an container obejct then ensure
        // the attribute "type" with value "list" is added to the node.
        var skip = false;
        skip = (from a in gn.Attributes()
                where a.Name.LocalName.ToLower() == "type"
                select a.Value.ToLower()).FirstOrDefault() == "list" ? true :
                   gn.Elements().GroupBy(n => n.Name.LocalName).Count() == 1;
 
        var p = result as IDictionary<String, dynamic>;
        var values = new List<dynamic>();
        // If the current node is a container node then we want to skip adding
        // the container node itself, but instead we load the children elements
        // of the current node. If the current node has child elements then load
        // those child elements recursively
        if (skip)
            foreach (var item in gn.Elements())
            {
                values.Add((item.HasElements) ? _getExpandoFromXml(null, item) :
                    item.Value.Trim());
            }
        else
            values.Add((gn.HasElements) ? _getExpandoFromXml(null, gn) :
                gn.Value.Trim());
 
        // Add the object name + value or value collection to the dictionary
        p[gn.Name.LocalName] = skip ? values : values.FirstOrDefault();
    }
    return result;
}
 

Example XML:
 
<?xml version="1.0"?>
 <License>
    <RegisteredUser>Remco Reitsma</RegisteredUser>
    <Company>Xtraworks.com</Company>
    <Sites type="list">
        <Site>
            <Host>xtraworks.com</Host>
            <ExpireDate>15/12/2099</ExpireDate>
        </Site>
        <Site>
            <Host>test.com</Host>
            <ExpireDate>15/12/2099</ExpireDate>
        </Site>
    </Sites>
    <Modules type="list">
        <Module>
            <Name>SEO Package</Name>
            <Controller>SEO</Controller>
            <Version>0.0.1</Version>
            <Tables>
                <Table>
                    <Name>SEO_Site</Name>
                </Table>
                <Table>
                    <Name>SEO_Pages</Name>
                </Table>
            </Tables>
        </Module>
    </Modules>
</License>
 
Usage
    
    dynamic license = _getExpandoFromXml('path to xml file');
    
    // Getting the values from the dynamic object is really easy now.
    var registeredUser = license.RegisteredUser;
    var companyName = license.Company;
 
    // Getting a collection is just as easy as it simply returns a list
    var sites = license.Sites;
    foreach(var site in sites)
    { 
       var host = site.Host;
    }
 
    // I am sure it's easy enough for you guys to extrapolate from this simple example.
Note:
One thing that is important to realize is that this is a very simple example and is by no means full-proof. The code as it is would be useful in cases where you have control over the XML files that are provided. Also the use of dynamic has some performance issues as internally it will need to do a lot of reflection during run time and you will also loose out on error checking of the dynamic objects during compiling.
 
It could be useful for example to read in a configuration file or a simple list of products.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

RemcoReitsma

Netherlands Netherlands
No Biography provided

Comments and Discussions

 
GeneralReally a good one Pinmembervikashevs5-Aug-13 3:24 
QuestionSerialization PinmemberBnR-Fouress13-Dec-12 1:56 
GeneralReally good stuff! but is not taking account of node attribu... PinmemberRinaldo J. Sassaroli13-Feb-12 13:26 
GeneralHow do I use the object once created? Like added values into... PinmemberMember 83291237-Nov-11 9:14 
GeneralRe: I have added a small example to the article which would help... PinmemberRemcoReitsma14-Dec-11 16:17 
GeneralAre you missing the keyword dynamic in your var p = result... Pinmembercechode18-Jul-11 10:13 
GeneralRe: Hmm something got messed up during copy and paste because fo... PinmemberRemcoReitsma18-Jul-11 15:49 
GeneralRe: Don't forget to vote up if you like the tip ;-) PinmemberRemcoReitsma14-Dec-11 19:28 
GeneralReason for my vote of 2 code produced a stackover flow excep... Pinmembercechode18-Jul-11 6:13 
GeneralRe: I've updated and simplified the code. There was indeed a bug... PinmemberRemcoReitsma18-Jul-11 9:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 14 Dec 2011
Article Copyright 2011 by RemcoReitsma
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid