Click here to Skip to main content
15,885,985 members
Articles / Web Development / ASP.NET
Tip/Trick

Creating Dynamic Object from XML using ExpandoObject and Put Intellisense using C#

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
14 Feb 2018CPOL2 min read 29.7K   507   7   3
Creating dynamic object from XML using ExpandoObject and put Intellisense using C#

Introduction

In my current project, we need to store a lot of constant values to run the system which can be achieved in different ways like using Resource File, Web.config, etc. We want those values to be environment specific (e.g., Development, Stage, QA, Production) and also Intellisense feature to use those values.

Existing approaches have the following limitations:

  • In case of config files, Intellisense feature is not available. Hence, there is always a probability of human error which is very difficult to find out.
  • In our scenario, there are cases where we want collection object to be populated from the constant values which are cumbersome in case of resource file approach used.
  • To enhance maintainability, values used for given context / module should be clubbed together which is difficult to achieve using existing approaches.

While searching for a better way, I found the .NET System.Dynamic.ExpandoObjec class, it will represent an object whose members can be dynamically added and removed at run time. I also found that an ExpandoObject was being used to represent an XML document. That is, while parsing, an ExpandoObject instance is created and each XML element is added as a member of the ExpandoObject. With a little more research, I found "ImpromptuInterface" package in NuGet. With the “ImpromptuInterface", any object can be wrapped with an interface.

So we decided to load the XML data into a dynamic type using the System.Dynamic.ExpandoObjec class.

Using the Code

For example, this XML: Constants.xml

XML
<?xml version="1.0" encoding="utf-8" ?>
<Settings>
  <Config>
    <AppData1>Data1</AppData1>
    <AppData2>Data2</AppData2>
    <SFTP>
      <UserName>TestUser</UserName>
      <Password>TestPwd</PassWord>
      <Port>22</Port>
    </SFTP>
  </Config>
</Settings>

The below line of code loads an XML file from disk.

C#
//
// Load an XML document
//
var xDoc = XDocument.Load(new StreamReader(xmlFileName));
...

The below section creates a list of XML node names that are known to contain lists of items. Once the XML is loaded, creation of the dynamic object can begin by calling Parse() method. Once the parsing is complete, the parsed data may then be used.

C#
//
// Convert the XML document in to a dynamic C# object.
// 
Parse(root, xDoc.Elements().First());
...

Below functions are used to load the XML to dynamic object in C#.

C#
public static dynamic GetConstantsData(string xmlFileName)
{
     dynamic root = new ExpandoObject();
     if (string.IsNullOrEmpty(xmlFileName))
     {
          return root;
     }
     // Load an XML document.
     var xDoc = XDocument.Load(new StreamReader(xmlFileName));

     // Convert the XML document in to a dynamic C# object.
     Parse(root, xDoc.Elements().First());
     return root;
}
...
C#
//
// Parse the XML document
//
private static void Parse(dynamic parent, XElement node)
{
   try
   {
      if (node.HasElements)
      {
         if (node.Elements(node.Elements().First().Name.LocalName).Count() > 1)
         {
            //list
            var item = new ExpandoObject();
            var list = new List<dynamic>();
            foreach (var element in node.Elements())
            {
               Parse(list, element);
            }
            AddProperty(item, node.Elements().First().Name.LocalName, list);
            AddProperty(parent, node.Name.ToString(), item);
         }else{
            var item = new ExpandoObject();
            foreach (var attribute in node.Attributes())
            {
               AddProperty(item, attribute.Name.ToString(), attribute.Value.Trim());
            }
            //element
            foreach (var element in node.Elements())
            {
               Parse(item, element);
            }
            AddProperty(parent, node.Name.ToString(), item);
         }
      }else{
         AddProperty(parent, node.Name.ToString(), node.Value.Trim());
      }
   }
   catch (Exception Ex)
   {
      throw new Exception(Ex.Message);
   }
}
...
C#
private static void AddProperty(dynamic parent, string name, object value)
{
     try
     {
          if (parent is List<dynamic>)
          {
               (parent as List<dynamic>).Add(value);
          }
          else
          {
               (parent as IDictionary<string, object>)[name] = value;
          }
     }
     catch (Exception Ex)
     {
          throw new Exception(Ex.Message);
     }
}
...

Install ImpromptuInterface

Open Package Manages Console Using Visual Studio Click Tools -> NuGet Package Manages -> Package Manages Console. Run the below command:

PM> Install-Package ImpromptuInterface
...

Defining Interfaces

Prior to adding the above dynamic behaviors to objects, we need to make sure the objects have interface methods. The interfaces for the sample XML are defined as follows:

C#
public interface IXML
{
    ISettings Settings { get; }
}

public interface ISettings
{
    IConfig Config { get; }        
}

public interface IConfig
{
    string AppData1 { get; set; }
    string AppData2{ get; set; }
    ISFTP SFTP { get; }        
}

public interface ISFTP
{
    string UserName { get; set; }
    string PassWord { get; set; }
    object Port { get; set; }
}
...

Wrapping Objects and Attaching Behaviors

The extension method ActLike<I> of object can be used to wrap an object with an interface.

C#
//
// To view the output run the below code
//
static void Main(string[] args)
{
    dynamic constantsData = XmlToDynamic.GetConstantsData("Constants.xml");
    IXML xml = ImpromptuInterface.Impromptu.ActLike(constantsData);

    Console.WriteLine("AppData1 = " + xml.Settings.Config.AppData1);
	Console.WriteLine("AppData2 = " + xml.Settings.Config.AppData2);
	Console.WriteLine("UserName = " + xml.Settings.Config.SFTP.UserName);
	Console.WriteLine("PassWord = " + xml.Settings.Config.SFTP.PassWord);
	Console.WriteLine("Port = " + xml.Settings.Config.SFTP.Port);
}

//
// Output
//

...

License

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


Written By
Software Developer (Senior)
India India
I have extensive professional experience with Microsoft technologies with an expertise in Microsoft .NET, C#, JavaScript, SQL, and ASP.NET. I also have an advanced understanding of the concepts behind these technologies including Object-Oriented Programming, Relational Data, MVC and MVVM.

Some of my more recent work has been designing and building web applications primarily with JavaScript in conjunction with many of the JavaScript libraries/frameworks including JQuery, AngularJS and Bootstrap and consuming both JSON and REST services.

Comments and Discussions

 
QuestionJust a small fix, I think Pin
Member 1327239918-Dec-18 15:02
Member 1327239918-Dec-18 15:02 
QuestionWhat is the advantage? Pin
Gokhan Mamaci14-Feb-18 22:33
professionalGokhan Mamaci14-Feb-18 22:33 
AnswerRe: What is the advantage? Pin
Vaso Elias19-Feb-18 2:46
Vaso Elias19-Feb-18 2:46 

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

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