Parsing complex JSON Object with C# - c#

I have a JSON like this one:
{
"name" : "MyCustomName",
"my_node" : {
"name" : "my_node_name"
},
"dict1":"value1",
"dict2":"value2",
"dict3":"value3",
...
}
and an object:
class Node{
string value;
}
class Sample:IDictionary<string, string>{
Node node;
string name;
}
Node and Name in Sample class are always present.
The thing is I don't know how many "dictN" fields will be... and that's the point.
And the question is:
How to Deserialize that JSON to this Object?

Edit: apparently even with field names harmonized, your deserializer just can't cope with specific fields combined with general dictionary fields.
In which case, I'd just advise deserializing as a Dictionary<string, object> and building with the result:
var d = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json);
Sample s = new Sample();
s.name = (string)d["name"];
Node n = new Node();
n.value = (string)((Dictionary<string, object>)d["my_node"])["name"];
foreach (var key in d.Keys.Except(new string[] { "name", "my_node" }))
{
s.Add(key, (string)d[key]);
}

INITIAL IDEA
The following is a dictionary serializer. It has one special case of not accepting empty string.
private void SerializePerinatalModel<T>(IDictionary<string, object> dataModel, T perinatalModel)
{
Type sourceType = typeof(T);
foreach (PropertyInfo propInfo in (sourceType.GetProperties()))
{
if (dataModel.ContainsKey(propInfo.Name))
{
// if an empty string has been returned don't change the value
if (dataModel[propInfo.Name].ToNullSafeString() != String.Empty)
{
try
{
Type localType = propInfo.PropertyType;
localType = Nullable.GetUnderlyingType(localType) ?? localType;
propInfo.SetValue(perinatalModel, Convert.ChangeType(dataModel[propInfo.Name], localType), null);
}
catch (Exception e)
{
// ToDo: log update value errors
}
}
}
}
}
but could be made null safe. It does deal with nullable types.
As JSON is essentially a dictionary type then iterating through the top level types should get you there.
This is written in haste so is only a sketch of an idea.
BETTER IDEA
Also try using
foreach (var item in JsonData.Where(m => m.Key.Substring(0,4) == "dict"))
{
// add item to collection
}
might also do the biz.

You can simply have the output in the form of Dictionary<string, object>, try this piece of code instead.
System.Web.Script.Serialization.JavaScriptSerializer s =
new System.Web.Script.Serialization.JavaScriptSerializer();
var nodes = s.Deserialize<Dictionary<string, object>>(jsonString);
var dictNodes = nodes.Where(w => w.Key.StartsWith("dict"));

Related

How to iterate through a dictionary to get and pass key name to string

I'd like to iterate through a C# dictionary storing nested JSON to retrieve and pass the dictionary key name to a string, in the form of "key1:key1-1:key1-1-1".
After that, a new dictionary is created to use the specially arranged string as its keys.
Finally, desiredDictionary["key:key:key"] = originalDictionary["key"]["key"]["key"].
Please make the answer specific, my apology that I'm new to C# IEnumerable class and JSON.
I've stored the JSON data into a dictionary, the sample JSON is given below.
using System.Web.Script.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
......
string jsonText = File.ReadAllText(myJsonPath);
var jss = new JavaScriptSerializer();
//this is the dictionary storing JSON
var dictJSON = jss.Deserialize<Dictionary<string, dynamic>>(jsonText);
//this is the dictionary with keys of specially arranged string
var desiredDict = new Dictionary<string, string>();
......
......
//Here is a sample JSON
{
"One": "Hey",
"Two": {
"Two": "HeyHey"
}
"Three": {
"Three": {
"Three": "HeyHeyHey"
}
}
}
I need help with the process for dictionary key name retrieval, string completion, and new dictionary value passing.
According to the given JSON, desiredDict["Three:Three:Three"] = dictJSON["Three"]["Three"]["Three"] = "HeyHeyHey",
The solution is expected to apply on any similar JSON.
You can use a recursive method to take a JObject and produce a flattened dictionary from it like so:
private static IDictionary<string, string> FlattenJObjectToDictionary(JObject obj)
{
// obtain a key/value enumerable and convert it to a dictionary
return NestedJObjectToFlatEnumerable(obj, null).ToDictionary(kv => kv.Key, kv => kv.Value);
}
private static IEnumerable<KeyValuePair<string, string>> NestedJObjectToFlatEnumerable(JObject data, string path = null)
{
// path will be null or a value like Three:Three: (where Three:Three is the parent chain)
// go through each property in the json object
foreach (var kv in data.Properties())
{
// if the value is another jobject, we'll recursively call this method
if (kv.Value is JObject)
{
var childDict = (JObject)kv.Value;
// build the child path based on the root path and the property name
string childPath = path != null ? string.Format("{0}{1}:", path, kv.Name) : string.Format("{0}:", kv.Name);
// get each result from our recursive call and return it to the caller
foreach (var resultVal in NestedJObjectToFlatEnumerable(childDict, childPath))
{
yield return resultVal;
}
}
else if (kv.Value is JArray)
{
throw new NotImplementedException("Encountered unexpected JArray");
}
else
{
// this kind of assumes that all values will be convertible to string, so you might need to add handling for other value types
yield return new KeyValuePair<string, string>(string.Format("{0}{1}", path, kv.Name), Convert.ToString(kv.Value));
}
}
}
Usage:
var json = "{\"One\":\"Hey\",\"Two\":{\"Two\":\"HeyHey\" },\"Three\":{\"Three\":{\"Three\":\"HeyHeyHey\"}}}";
var jObj = JsonConvert.DeserializeObject<JObject>(json);
var flattened = FlattenJObjectToDictionary(jObj);
It takes advantage of yield return to return the results of the recursive call as a single IEnumerable<KeyValuePair<string, string>> and then returns that as a flat dictionary.
Caveats:
Arrays in JSON will throw an exception (see else if (kv.Value is JArray))
The else part for handling the actual values assumes that all values are convertible to string using Convert.ToString(kv.Value). If this is not the case, you will have to code for extra scenarios.
Try it online

How to implement ForEach statement on Newtonsoft JArray

I need to cast JArray which I received from browser to list of MyType object. The problem is that I don't want to use .ToObject<> extension because object sended from browser can have missing or extra values and I cannot change deserializer. I wrote my own parser to receive data from single JObject. Usage:
foreach (var _object in _jarray)
{
using (var tp = new TokenParser(_object))
{
MyType test = new MyType()
{
id = tp.ConvertToInt("token_key")
};
}
}
What I want to do is something like this
_jarray.ForEach(_object => {
using (var tp = new TokenParser(_object))
{
MyType test = new MyType()
{
id = tp.ConvertToInt("token_key")
};
}
});
I don't want to cast elements like:
_jarray.Select(x =>
(int)["token_key"]
);
I tried to wrote this extension based on LINQ equivalent of foreach for IEnumerable<T> but it seems that Action requires T as parameter type. I don't need this because JArray is an array of JObject.
Is this possible to achieve?

Cannot retrieve the key and Value info from the deserialized data

dynamic data = JsonConvert.DeserializeObject(result);
foreach (var field in data)
{
var deserializedData = new DeserializedData
{
Label = field.Key.ToObject<string>(),
Content = field.Value.ToObject<string>()
};
}
Note: data = {"name":"test"}
Error:'Newtonsoft.Json.Linq.JProperty' does not contain a definition for 'Key'
I think I understand what the issue is but I am not sure if there is a way to achieve this behavior or is there a different approach to handle the dynamic key value scenarios like this.
The Key will change so I don't know if its going to be name or phone or email or some other field...
In order to iterate through the children of the returned data as an IEnumerable<KeyValuePair<string, JToken>>, you need to explicitly declare the deserialized object as a JObject:
var data = JObject.Parse(result);
foreach (var field in data)
{
var deserializedData = new DeserializedData
{
Label = field.Key,
Content = field.Value.ToObject<string>()
};
}
When you do dynamic data = JsonConvert.DeserializeObject(result);, the foreach ends up calling the IEnumerable<JToken> iterator in the base class JToken. I.e. the following also works:
dynamic data = JsonConvert.DeserializeObject(result);
foreach (var field in data)
{
// "field" happens to be of type JProperty
var deserializedData = new DeserializedData
{
Label = field.Name,
Content = field.Value.ToObject<string>()
};
}
Though I prefer the strongly typed version using JObject since more checking can be done at compile time.
According to the documentation the JProperty's value contains the key, and the JProperty's children are the values. source

Are there any JSON serializers for C# .Net 4.0+ that serialize JSON to Dictionary<string,dynamic> and dynamic[]

I know you can serialize JSON to Dictionary and object[], but the problem is that it requires you to constantly cast things all over the place:
using System.Web.Script.Serialization;
var json = new JavaScriptSerializer { }.DeserializeObject(#"{array:['item1','item2']}") as Dictionary<string, object>;
var strings = new List<string> { };
if (json != null)
{
var array = json["array"] as object[];
if (array != null)
{
foreach (var item in array.Select(i=>i as string))
{
if (!string.IsNullOrWhiteSpace(item))
{
strings.Add(item);
}
}
}
}
return strings.ToArray();
I would much rather do something more like:
try
{
var json = new JavaScriptSerializer { }.DeserializeDynamic(#"{array:['item1','item2']}") as Dictionary<string, dynamic>;
var strings = new List<string>{};
foreach (var item in json["array"])
strings.Add(item as string);
return strings.ToArray();
}
catch { return null; }
I have run into problems with object[] and number types, I'm never sure if it's going to be an int, a float, a decimal, or a double etc. I can't sanitize my json data so it reliably is only a double...
Are there any JSON Serializers for .Net 4.0 that output Dictionary and dynamic[]? That just seams like a more natural choice for working with json then Dictionary and object[]...
JSON.NET should handle your needs (with the Json.NET Dynamic Extensions).
Dictionary<string, object> and Dictionary<string, dynamic> are the same types as far runtime is concerned.
For example this works:
var dynDic =new Dictionary<string,object>{ {"test", 1}}
as Dictionary<string,dynamic>;
int value = dynDic["test"];
So if your first works:
var json = new JavaScriptSerializer { }
.DeserializeObject(#"{array:['item1','item2']}")
as Dictionary<string, object>;
then so should:
var json = new JavaScriptSerializer { }
.DeserializeDynamic(#"{array:['item1','item2']}")
as Dictionary<string, dynamic>;
But then :
foreach (var item in json["array"])
strings.Add(item as string);
doesn't use dynamic invocation so it compiles that same whether the item var was object or dynamic so if you are having problems my guess is it's in the use of dynamic.
Same rule applies to object[] and dynamic[], they are the same type, you can freely cast between them, when you cast you are just telling the compiler how to use the objects from that array.
dynamic[] dynArr = new object[]{1};
So the answer is that anything that will deserialize to Dictionary<string,object> or object[] can be used as Dictionary<string,dynamic> or dynamic[]

Array of dynamic | ExpandoObject | with a compressed initialize syntax

Im trying to use DynamicObject in c#, and I needed an array of dynamic:
var d = new dynamic[];
which works fine.
EDIT : See ExpandoObject below.
But I also like to fill that array with some data with this compressed initialize new syntax:
var d = new dynamic[] {
new {
Name = "Some",
Number = 1010
},
new {
Name = "Other",
Number = 2010
}
}
But in that case all objects gets the non-dynamic type "object" and a loop through the items will give me an exception:
foreach (dynamic item in d)
{
#item.Name
#item.Number
}
Error : 'object' does not contain a definition for 'Name'. I guess I just initialize the array items the wrong way. How to add dynamic objects instead?
EDIT: New content:
I realize "dynamic" does not have the capability to dynamically add properties.
I better be using ExpandoObject which exposes all items in an an internal dictionary as properties. But unfortunately ExpandoObject does not seem to support this nice compressed create syntax, and the compiler complains:
var d = new ExpandoObject[]{
new ExpandoObject(){
Name="Nnn",
Number=1080
}
}
So the answer might just be : it's not possible.
Hopefully you do not really need dynamics
class Program
{
static void Main(string[] args)
{
var d = new[]
{
new
{
Name = "Some",
Number = 1010
},
new
{
Name = "Other",
Number = 2010
}
};
foreach (var item in d)
{
string s = #item.Name;
int n = #item.Number;
Console.WriteLine("{0} {1}", s, n);
}
}
}
I come a bit late but here is what i found about it :
if I can't initialise an ExpandoObject, how about initialising it with a dynamic type ?
so i did the following extension method
public static ExpandoObject CreateExpando(this object item)
{
var dictionary = new ExpandoObject() as IDictionary<string, object>;
foreach (var propertyInfo in item.GetType().GetProperties())
{
dictionary.Add(propertyInfo.Name, propertyInfo.GetValue(item, null));
}
return (ExpandoObject)dictionary;
}
I know it's far from ideal, but it's the best I could achieve for now, it works like this :
var myExpandoObject = new { Name="Alex", Age=30}.CreateExpando();
The open source framework Impromptu-Interface has a compressed initialization syntax that works with ExpandoObject.
dynamic #new = Builder.New<ExpandoObject>();
var d = #new.List(
#new.Expando(
Name:"Some",
Number: 1010
),
#new.Expando(
Name:"Other",
Number: 2010
)
);

Categories

Resources