Deserialize/Parse JSON and use it for LOOKUP

tommym121
tommym121 used Ask the Experts™
on
I am trying to read in a JSON  stream and able to use it to look up the datatype of a property. please see below.
What do I need to call using JSON.net to structure a proper lookup of an array of elements,  Below is a summary what I would like to do:
1. Lookup to see if there is a match of one of the following keyword:- 'Device/AnalogInput/AnalogOutput ...'.  This list can vary at runtime hence I can not write a class for each keyword
2. if exist,  loop through the property to find out the data type of each property
For example
I check if there is a keyword called 'Device',  Once I find it exists, I would like to loop through to find the 'datatype' of each property. In this case, I would find out 'datatype' of property 'SystemStatus' is 'int'. Next, I would find out 'datatype' of property 'ObjectName' is 'string'.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Data;


namespace TestJSONLookup
{
    class Program
    {
        static void Main(string[] args)
        {
            string json = @"[
  {
    'Device': [
      {
        'property': 'SystemStatus',
        'dataType': 'int'
      },
      {
        'property': 'ObjectName',
        'dataType': 'string'
      }
    ]
  },
  {
    'AnalogInput': [
      {
        'property': 'PresentValue',
        'type': 'real'
      }
    ]
  },
  {
    'AnalogOutput' : [
      {
        'property': 'PresentValue',
        'type': 'real'
      }
    ]
	},
	{
		'AnalogValue': [
			{ 'property': 'PresentValue', 'type': 'real' },
			{ 'property': 'Status', 'type': 'int' }
		]
	},
	{
		'BinaryInput' : [
			{ 'property': 'PresentValue', 'type': 'bool' }					
		]
	},
  {
		'BinaryOutput' : [
			{ 'property': 'PresentValue', 'type': 'bool' }					
		]
	},
	{
		'BinaryValue' : [
			{ 'property': 'PresentValue', 'type': 'bool' }
		]
	},
	{
		'MultistateInput' : [
			{ 'property': 'PresentValue', 'type': 'real' }					
		]
  },
	{	
		'MultistateOutput' : [
			{ 'property': 'PresentValue', 'type': 'real' }					
		]
	},
	{
		'MultistateValue' : [
			{ 'property': 'PresentValue', 'type': 'real' },
			{ 'property': 'Status', 'type': 'int' }
		]
	}
]";

            a = JArray.Parse(json);
            // not sure what is next statement to setup the lookup
            //JToken t = a[0];
            //JObject o = t;
            //Console.WriteLine(a.ToString());
        }
    }
}

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Commented:
You could do something like this:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EE_Q29124178
{
	class Program
	{
		static string json = @"[{
			'Device':[{
				'property':'SystemStatus',
				'dataType':'int'
			},{
				'property':'ObjectName',
				'dataType':'string'
			}]
		},{
			'AnalogInput':[{
				'property':'PresentValue',
				'dataType':'real'
			}]
		},{
			'AnalogOutput':[{
				'property':'PresentValue',
				'dataType':'real'
			}]
		},{
			'AnalogValue':[{
				'property':'PresentValue',
				'dataType':'real'
			},{
				'property':'Status',
				'dataType':'int'
			}]
		},{
			'BinaryInput':[{
				'property':'PresentValue',
				'dataType':'bool'
			}]
		},{
			'BinaryOutput':[{
				'property':'PresentValue',
				'dataType':'bool'
			}]
		},{
			'BinaryValue':[{
				'property':'PresentValue',
				'dataType':'bool'
			}]
		},{
			'MultistateInput':[{
				'property':'PresentValue',
				'dataType':'real'
			}]
		},{
			'MultistateOutput':[{
				'property':'PresentValue',
				'dataType':'real'
			}]
		},{
			'MultistateValue':[{
				'property':'PresentValue',
				'dataType':'real'
			},{
				'property':'Status',
				'dataType':'int'
			}]
		}]";

		static void Main(string[] args)
		{
			var keywords = @"Device/AnalogInput/AnalogOutput";
			var terms = keywords.Split('/');
			var definitions = Newtonsoft.Json.JsonConvert.DeserializeObject<JObject[]>(json);
			var results = (from term in terms
						   from definition in definitions.Where(d => d.ContainsKey(term))
						   from child in definition[term].Children()
						   group child by term into grp
						   select new KeyValuePair<string, List<Definition>>(grp.Key, (from d in grp select new Definition { Property = d["property"].Value<string>(), DataType = d["dataType"].Value<string>() }).ToList())).ToDictionary(k => k.Key, v => v.Value);
			foreach (var result in results)
			{
				foreach (var definition in result.Value)
				{
					Console.WriteLine($"Key: {result.Key}; Value: {definition}");
				}
			}
			Console.ReadLine();
		}
	}

	class Definition
	{
		public string Property { get; set; }
		public string DataType { get; set; }

		public override string ToString()
		{
			return $"{{ {string.Join(", ", from prop in GetType().GetProperties() select $"{prop.Name}: {prop.GetValue(this, null)}")} }}";
		}
	}
}

Open in new window

Which produces the following output -Capture.PNG-saige-
Commented:
Another option would be just to create a dictionary from the JSON and then simply get the List of definitions from the key; e.g. -
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EE_Q29124178
{
	class Program
	{
		static string json = @"[{
			'Device':[{
				'property':'SystemStatus',
				'dataType':'int'
			},{
				'property':'ObjectName',
				'dataType':'string'
			}]
		},{
			'AnalogInput':[{
				'property':'PresentValue',
				'dataType':'real'
			}]
		},{
			'AnalogOutput':[{
				'property':'PresentValue',
				'dataType':'real'
			}]
		},{
			'AnalogValue':[{
				'property':'PresentValue',
				'dataType':'real'
			},{
				'property':'Status',
				'dataType':'int'
			}]
		},{
			'BinaryInput':[{
				'property':'PresentValue',
				'dataType':'bool'
			}]
		},{
			'BinaryOutput':[{
				'property':'PresentValue',
				'dataType':'bool'
			}]
		},{
			'BinaryValue':[{
				'property':'PresentValue',
				'dataType':'bool'
			}]
		},{
			'MultistateInput':[{
				'property':'PresentValue',
				'dataType':'real'
			}]
		},{
			'MultistateOutput':[{
				'property':'PresentValue',
				'dataType':'real'
			}]
		},{
			'MultistateValue':[{
				'property':'PresentValue',
				'dataType':'real'
			},{
				'property':'Status',
				'dataType':'int'
			}]
		}]";

		static void Main(string[] args)
		{
			var keywords = @"Device/AnalogInput/AnalogOutput";
			var terms = keywords.Split('/');
			var sets = Newtonsoft.Json.JsonConvert.DeserializeObject<JObject[]>(json);
			var dictionary = (from set in sets
							  let term = set.Properties().First().Name
							  from child in set[term].Children()
							  group child by term into grp
							  select new KeyValuePair<string, List<Definition>>(grp.Key, (from d in grp select new Definition { Property = d["property"].Value<string>(), DataType = d["dataType"].Value<string>() }).ToList())).ToDictionary(k => k.Key, v => v.Value);

			foreach (var term in terms)
			{
				List<Definition> definitions;
				if (dictionary.TryGetValue(term, out definitions))
				{
					foreach (var definition in definitions)
					{
						Console.WriteLine($"Key: {term}; Value: {definition}");
					}
				}
				else
				{
					Console.WriteLine($"Dictionary contained no values for {term}");
				}
			}
			Console.ReadLine();
		}
	}

	class Definition
	{
		public string Property { get; set; }
		public string DataType { get; set; }

		public override string ToString()
		{
			return $"{{ {string.Join(", ", from prop in GetType().GetProperties() select $"{prop.Name}: {prop.GetValue(this, null)}")} }}";
		}
	}
}

Open in new window

Which would produce the same output as above.

-saige-

Author

Commented:
Thank you.  This is what I miss.   If you have a moment, please give me some explanation on what this statement work.  Some good reference to explain this kind of structures will be good.  Thanks for your help.  

var dictionary = (from set in sets
                                            let term = set.Properties().First().Name
                                            from child in set[term].Children()
                                            group child by term into grp
                                            select new KeyValuePair<string, List<Definition>>(grp.Key, (from d in grp select new Definition { Property = d["property"].Value<string>(), DataType = d["dataType"].Value<string>() }).ToList())).ToDictionary(k => k.Key, v => v.Value);
Commented:
This is a LINQ statment.  Here is the same logic written without using LINQ:
var dictionary = new Dictionary<string, List<Definition>>();
foreach (var set in sets)
{
	var term = set.Properties().First().Name;
	var grp = new List<Definition>();
	foreach (var child in set[term].Children())
	{
		grp.Add(new Definition { Property = child["property"].Value<string>(), DataType = child["property"].Value<string>() });
	}
	dictionary.Add(term, grp);
}

Open in new window

-saige-

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial