use Newtonsoft to parse Configuration files in .net 5 - c#

I have a json configuration file where I would like to store a json object that could hold several unknown fields.
I can deserialize such an object using Newtonsoft by deriving from this base class:
public class ComplexJsonObject
{
[JsonExtensionData]
private IDictionary<string, JToken> _additionalData_newtonsoft;
}
Unfortunately it seems that the config file appsettings.development.json just says I have an
empty object. Even though there is something configured.
I assumed this was because the system used System.Text.Json.
So I tried that as well:
public class ComplexJsonObject
{
[JsonExtensionData]
private IDictionary<string, JToken> _additionalData_newtonsoft;
[System.Text.Json.Serialization.JsonExtensionData]
[Newtonsoft.Json.JsonIgnore]
public IDictionary<string, JsonElement> _additionalData_dotnet { get; set; }
}
This does not work either.
So the question: how do I tell the system to use Newtonsoft for deserializing this config file?
-- edit --
As requested, an example of the config I would like to store.
The config key would be "configuration:when" and the object I expect must have a operator, but all the other fields are dynamic.
{
"extraction": {
"when": {
"operator": "and",
"rules": [
{
"operator": "or",
"rules": [
{ "operator": "field.contains", "value": "waiting" },
{ "operator": "field.contains", "value": "approved" },
{ "operator": "field.contains", "value": "rejected" }
]
},
{ "operator": "not", "rule": { "operator": "field.contains", "value": "imported" } },
{ "operator": "not", "rule": { "operator": "field.contains", "value": "import-failed" } }
]
}
}
}
I think Métoule is correct, and this is indeed not possible. Since the config by default would mix values from other files.

If you need to convert just a specific section using newtonsoft, you can use this extension:
public static class ConfigurationBinder
{
public static void BindJsonNet<T>(this IConfiguration config, T instance) where T : class
{
string objectPath = config.AsEnumerable().Select(x => x.Key).FirstOrDefault();
var obj = BindToExpandoObject(config, objectPath);
if (obj == null)
return;
var jsonText = JsonConvert.SerializeObject(obj);
var jObj = JObject.Parse(jsonText);
if (jObj == null)
return;
var jToken = jObj.SelectToken($"*.{GetLastObjectName(objectPath)}");
if (jToken == null)
return;
jToken.Populate<T>(instance);
}
private static ExpandoObject BindToExpandoObject(IConfiguration config, string objectPath)
{
var result = new ExpandoObject();
string lastObjectPath = GetLastObjectPath(objectPath);
// retrieve all keys from your settings
var configs = config.AsEnumerable();
configs = configs
.Select(x => new KeyValuePair<string, string>(x.Key.Replace(lastObjectPath, ""), x.Value))
.ToArray();
foreach (var kvp in configs)
{
var parent = result as IDictionary<string, object>;
var path = kvp.Key.Split(':');
// create or retrieve the hierarchy (keep last path item for later)
var i = 0;
for (i = 0; i < path.Length - 1; i++)
{
if (!parent.ContainsKey(path[i]))
{
parent.Add(path[i], new ExpandoObject());
}
parent = parent[path[i]] as IDictionary<string, object>;
}
if (kvp.Value == null)
continue;
// add the value to the parent
// note: in case of an array, key will be an integer and will be dealt with later
var key = path[i];
parent.Add(key, kvp.Value);
}
// at this stage, all arrays are seen as dictionaries with integer keys
ReplaceWithArray(null, null, result);
return result;
}
private static string GetLastObjectPath(string objectPath)
{
string lastObjectPath = objectPath;
int indexLastObj;
if ((indexLastObj = objectPath.LastIndexOf(":")) != -1)
lastObjectPath = objectPath.Remove(indexLastObj);
return lastObjectPath;
}
private static string GetLastObjectName(string objectPath)
{
string lastObjectPath = objectPath;
int indexLastObj;
if ((indexLastObj = objectPath.LastIndexOf(":")) != -1)
lastObjectPath = objectPath.Substring(indexLastObj + 1);
return lastObjectPath;
}
private static void ReplaceWithArray(ExpandoObject parent, string key, ExpandoObject input)
{
if (input == null)
return;
var dict = input as IDictionary<string, object>;
var keys = dict.Keys.ToArray();
// it's an array if all keys are integers
if (keys.All(k => int.TryParse(k, out var dummy)))
{
var array = new object[keys.Length];
foreach (var kvp in dict)
{
array[int.Parse(kvp.Key)] = kvp.Value;
}
var parentDict = parent as IDictionary<string, object>;
parentDict.Remove(key);
parentDict.Add(key, array);
}
else
{
foreach (var childKey in dict.Keys.ToList())
{
ReplaceWithArray(input, childKey, dict[childKey] as ExpandoObject);
}
}
}
public static void Populate<T>(this JToken value, T target) where T : class
{
using (var sr = value.CreateReader())
{
JsonSerializer.CreateDefault().Populate(sr, target); // Uses the system default JsonSerializerSettings
}
}
}
Usage:
var obj = new SampleObject();
Configuration.GetSection("test:arr:3:sampleObj").BindJsonNet(obj);
services.AddSingleton(obj);
Reference:
https://stackoverflow.com/a/55050425/7064571
Bind netcore IConfigurationSection to a dynamic object

What you want is not possible, because that's not how the .NET configuration system works. The configuration doesn't directly parse your JSON into your data structure; instead, it creates an intermediate representation (which is a simple IDictionary<string,string>) which is then bound to your data structure.
The reason why it's not a direct mapping is because the configuration data can come from multiple sources. For example, it's possible to override the JSON configuration with values specified via the Azure portal UI. Or there might not be a JSON file at all.
That being said, it's possible to abuse the configuration system, like I explained in the following questions:
How to load polymorphic objects in appsettings.json
Bind netcore IConfigurationSection to a dynamic object

Related

Trouble reading a nested structure

I'm trying to read a JSON file using Unity's JSON utility, and the file looks like this:
{ "entries": [{
"2019": [{
"january": [{
"6": [{
"name": "Litago",
"ingredients": [{
"kaloriar": "20",
"salt": "10"
}]
}]
}]
}]
}]
}
I'm struggling a bit with how I should set up my nested classes. I'm currently doing this, but it's not working.
[System.Serializable]
public class Entries
{
public KeyValuePair<string, List<Year>> Year;
}
[System.Serializable]
public class Year
{
public KeyValuePair<string, List<Month>> Month;
}
[System.Serializable]
public class Month
{
public KeyValuePair<string, List<Day>> Day;
}
[System.Serializable]
public class Day
{
public KeyValuePair<string, List<Meal>> Meal;
}
[System.Serializable]
public class Meal
{
public string Name;
public List<KeyValuePair<string, string>> ingredients;
}
I read the JSON like so:
Entries entries = JsonUtility.FromJson<Entries>(JSONString);
Ideally, I would like to do something like:
Debug.Log(entries["2019"]["January"]["6"]["name"]); // Should print "Litago"
but as my classes are most likely not setup correctly, I get type errors. Any ideas would be appreciated, and suggestions for other better plugins to read the JSON is welcome!
To answer your question, how to access the data, you can simply do the following to access what you need.
JObject entries = JObject.Parse(jsonString);
Console.WriteLine(entries["entries"][0]["2019"][0]["january"][0]["6"][0]["ingredients"][0]["kaloriar"].ToString());
Output
20
Create Dictionary of Dictionary...
You can create a recursive method to build out your Dictionary<string, object> items. Reason it has to be Dictionary of objects is because you have dynamic values each time you go in sub node.
public static Dictionary<string, object> BuildDictionary(JObject input)
{
var properties = input.Properties();
// Terminator
if (properties.ToList().Where(x => x.Name.Equals("name")).Count() > 0)
{
Day thisDay = new Day()
{
name = input["name"].ToString(),
ingredients = new Ingredients()
{
kaloriar = input["ingredients"][0]["kaloriar"].ToString(),
salt = input["ingredients"][0]["salt"].ToString()
}
};
return new Dictionary<string, object>() { { "Meal", thisDay } };
}
// Recursive
Dictionary<string, object> obj = new Dictionary<string, object>();
foreach (JProperty property in properties)
{
foreach (var element in input[property.Name])
obj.Add(property.Name, BuildDictionary(element as JObject));
}
return obj;
}
Usage in Main
JObject entries = JObject.Parse(jsonString);
Dictionary<string, object> dict = BuildDictionary(entries);
Resulting Dictionary
{
"entries": {
"2019": {
"january": {
"6": {
"Meal": {
"name": "Litago",
"ingredients": {
"kaloriar": "20",
"salt": "10"
}
}
}
}
}
}
}
And you can access the data you are looking for very similarly to what you wre looking for.
Console.WriteLine(JObject.Parse(JsonConvert.SerializeObject(dict))["entries"]["2019"]["january"]["6"]["Meal"]["ingredients"]["kaloriar"].ToString());
Output
20
In essence what you are doing is taking the array of elements and converting only elements into dictionaries for accessing the way you want.
If you are open to reverting to Json.Net library, you could leverage it's extensibility points to build something very similar to your desired construct:
Override [] operator on a List<T> so it allows for string input and make chaining look more natural.
Override ExpandoObjectConverter that comes with Json.Net so it injects your custom lists instead of default.
Overall the code might look something like this:
public class SearchableList<T> : List<T>
{
public object this[string item]
{
get {
var listItem = this.Cast<IDictionary<string, object>>().First(l => l.ContainsKey(item)); // I am assuming that your top level array items will only have one matching key
return listItem[item];
}
}
}
public class MyConverter : ExpandoObjectConverter
{
static bool IsPrimitiveToken(JsonToken token)
{
if ((uint)(token - 7) <= 5u || (uint)(token - 16) <= 1u)
{
return true;
}
return false;
}
bool MoveToContent(JsonReader reader)
{
JsonToken tokenType = reader.TokenType;
while (tokenType == JsonToken.None || tokenType == JsonToken.Comment)
{
if (!reader.Read())
{
return false;
}
tokenType = reader.TokenType;
}
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return ReadValue(reader);
}
private object ReadValue(JsonReader reader)
{
if (!MoveToContent(reader))
{
throw new JsonSerializationException("Unexpected end when reading ExpandoObject.");
}
switch (reader.TokenType)
{
case JsonToken.StartObject:
return ReadObject(reader);
case JsonToken.StartArray:
return ReadList(reader);
default:
if (IsPrimitiveToken(reader.TokenType))
{
return reader.Value;
}
throw new JsonSerializationException("Unexpected token when converting ExpandoObject");
}
}
private object ReadList(JsonReader reader)
{
IList<object> list = new SearchableList<object>(); // it is quite unfortunate to have to reimplement all class just because of this one line.
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.EndArray:
return list;
case JsonToken.Comment:
continue;
}
object item = ReadValue(reader);
list.Add(item);
}
throw new JsonSerializationException("Unexpected end when reading ExpandoObject.");
}
private object ReadObject(JsonReader reader)
{
IDictionary<string, object> dictionary = new ExpandoObject();
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.PropertyName:
{
string key = reader.Value.ToString();
if (!reader.Read())
{
throw new JsonSerializationException("Unexpected end when reading ExpandoObject.");
}
object obj2 = dictionary[key] = ReadValue(reader);
break;
}
case JsonToken.EndObject:
return dictionary;
}
}
throw new JsonSerializationException("Unexpected end when reading ExpandoObject.");
}
}
void Main()
{
var myConverter = new MyConverter();
dynamic entries = JsonConvert.DeserializeObject<ExpandoObject>("your json here", myConverter);
Console.WriteLine(entries.entries["2019"]["january"]["6"]["name"]);
}
you will notice, MyConverter has a lot of seemingly unrelated code, which is a bit unfortunate consequence of how ExpandoObjectConverter has pretty limited extensibility out of the box. You could potentially do just with stock standard ExpandoObjectConverter but the object it produces gets a bit awkward to traverse given your source json format.
Hopefully this gives you an avenue to explore.

Newton JSON serialize complex key collection into JSON

I have the below dictionary, and I need to convert the complex key values into proper JSON.
static void Main(string[] args)
{
Dictionary<string, object> collectionProp = new Dictionary<string, object>();
Dictionary<string, object> prop = new Dictionary<string, object>();
prop.Add("content", "button");
prop.Add("page.count", "10");
prop.Add("columns.0.textAlign", "center");
prop.Add("columns.1.textAlign", "left");
var result = new Dictionary<string, object>();
foreach (var pair in prop)
{
var key = pair.Key;
var parts = key.Split('.');
var currentObj = result;
for (int i = 0; i < parts.Length - 1; i++)
{
var property = parts[i];
if (!currentObj.Keys.Contains(property))
currentObj[property] = new Dictionary<string, object>();
currentObj = (Dictionary<string, object>)currentObj[property];
}
currentObj[parts[parts.Length - 1]] = pair.Value;
}
Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
Console.ReadLine();
}
And getting the following result.
{
"content": "button",
"page": {
"count": "10"
},
"columns": {
"0": {
"textAlign": "center"
},
"1": {
"textAlign": "left"
}
}
}
Expecting the columns should be grouped as JSON array. How to achieve this.?
The desired output:
{
"content": "button",
"page": {
"count": "10"
},
"columns": [
{
"textAlign": "center"
},
{
"textAlign": "left"
}
]
}
JSON.NET will serialize dictionaries as JSON objects by default even though its keys are convertible to an integer. Instead of building a dictionary from the source you could build JObject hierarchy. These helper methods recognize array indices indices in your path to automatically build JArray container around them:
public static class JsonExtensions
{
public static void SetValue(this JContainer container, string path, object value)
{
JToken token = container;
var keys = path.Split('.');
foreach (var key in keys)
{
int index;
if (int.TryParse(key, out index))
{
var jArray = token as JArray;
if (jArray == null)
{
jArray = new JArray();
token.Replace(jArray);
token = jArray;
}
while (index >= jArray.Count)
{
jArray.Add(JValue.CreateNull());
}
token = jArray[index];
}
else
{
var jObject = token as JObject;
if (jObject == null)
{
jObject = new JObject();
token.Replace(jObject);
token = jObject;
}
token = token[key] ?? (token[key] = JValue.CreateNull());
}
}
token.Replace(new JValue(value));
}
public static void SetValues(this JContainer container, IEnumerable<KeyValuePair<string, object>> pairs)
{
foreach (var pair in pairs)
{
container.SetValue(pair.Key, pair.Value);
}
}
}
And here's how you get the results you expect:
var jObject = new JObject();
jObject.SetValues(new Dictionary<string, object>
{
{ "content", "button" },
{ "page.count", "10" },
{ "columns.0.textAlign", "center" },
{ "columns.1.textAlign", "left" }
});
Console.WriteLine(jObject.ToString(Formatting.Indented));
Note that the code sample I provided is no way near efficient and should be used just as an inspiration to achieve the result you require.
Also note that in some cases the order of values to build the JObject matters, but enumerating items from dictionary is non-deterministic. Therefore you might consider a better data structure for the source that guarantees order of key-value pairs in it such as an array.

JSON string to C# class object [duplicate]

Is there a way to deserialize JSON content into a C# dynamic type? It would be nice to skip creating a bunch of classes in order to use the DataContractJsonSerializer.
If you are happy to have a dependency upon the System.Web.Helpers assembly, then you can use the Json class:
dynamic data = Json.Decode(json);
It is included with the MVC framework as an additional download to the .NET 4 framework. Be sure to give Vlad an upvote if that's helpful! However if you cannot assume the client environment includes this DLL, then read on.
An alternative deserialisation approach is suggested here. I modified the code slightly to fix a bug and suit my coding style. All you need is this code and a reference to System.Web.Extensions from your project:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
public sealed class DynamicJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
}
#region Nested type: DynamicJsonObject
private sealed class DynamicJsonObject : DynamicObject
{
private readonly IDictionary<string, object> _dictionary;
public DynamicJsonObject(IDictionary<string, object> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_dictionary = dictionary;
}
public override string ToString()
{
var sb = new StringBuilder("{");
ToString(sb);
return sb.ToString();
}
private void ToString(StringBuilder sb)
{
var firstInDictionary = true;
foreach (var pair in _dictionary)
{
if (!firstInDictionary)
sb.Append(",");
firstInDictionary = false;
var value = pair.Value;
var name = pair.Key;
if (value is string)
{
sb.AppendFormat("{0}:\"{1}\"", name, value);
}
else if (value is IDictionary<string, object>)
{
new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
}
else if (value is ArrayList)
{
sb.Append(name + ":[");
var firstInArray = true;
foreach (var arrayValue in (ArrayList)value)
{
if (!firstInArray)
sb.Append(",");
firstInArray = false;
if (arrayValue is IDictionary<string, object>)
new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
else if (arrayValue is string)
sb.AppendFormat("\"{0}\"", arrayValue);
else
sb.AppendFormat("{0}", arrayValue);
}
sb.Append("]");
}
else
{
sb.AppendFormat("{0}:{1}", name, value);
}
}
sb.Append("}");
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!_dictionary.TryGetValue(binder.Name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = WrapResultObject(result);
return true;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1 && indexes[0] != null)
{
if (!_dictionary.TryGetValue(indexes[0].ToString(), out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = WrapResultObject(result);
return true;
}
return base.TryGetIndex(binder, indexes, out result);
}
private static object WrapResultObject(object result)
{
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
return new DynamicJsonObject(dictionary);
var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
return arrayList[0] is IDictionary<string, object>
? new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)))
: new List<object>(arrayList.Cast<object>());
}
return result;
}
}
#endregion
}
You can use it like this:
string json = ...;
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
dynamic obj = serializer.Deserialize(json, typeof(object));
So, given a JSON string:
{
"Items":[
{ "Name":"Apple", "Price":12.3 },
{ "Name":"Grape", "Price":3.21 }
],
"Date":"21/11/2010"
}
The following code will work at runtime:
dynamic data = serializer.Deserialize(json, typeof(object));
data.Date; // "21/11/2010"
data.Items.Count; // 2
data.Items[0].Name; // "Apple"
data.Items[0].Price; // 12.3 (as a decimal)
data.Items[1].Name; // "Grape"
data.Items[1].Price; // 3.21 (as a decimal)
It's pretty simple using Json.NET:
dynamic stuff = JsonConvert.DeserializeObject("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
string name = stuff.Name;
string address = stuff.Address.City;
Also using Newtonsoft.Json.Linq:
dynamic stuff = JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
string name = stuff.Name;
string address = stuff.Address.City;
Documentation: Querying JSON with dynamic
You can do this using System.Web.Helpers.Json - its Decode method returns a dynamic object which you can traverse as you like.
It's included in the System.Web.Helpers assembly (.NET 4.0).
var dynamicObject = Json.Decode(jsonString);
.NET 4.0 has a built-in library to do this:
using System.Web.Script.Serialization;
JavaScriptSerializer jss = new JavaScriptSerializer();
var d = jss.Deserialize<dynamic>(str);
This is the simplest way.
Simple "string JSON data" to object without any third-party DLL file:
WebClient client = new WebClient();
string getString = client.DownloadString("https://graph.facebook.com/zuck");
JavaScriptSerializer serializer = new JavaScriptSerializer();
dynamic item = serializer.Deserialize<object>(getString);
string name = item["name"];
//note: JavaScriptSerializer in this namespaces
//System.Web.Script.Serialization.JavaScriptSerializer
Note: You can also using your custom object.
Personel item = serializer.Deserialize<Personel>(getString);
You can achieve that with the help of Newtonsoft.Json. Install it from NuGet and then:
using Newtonsoft.Json;
dynamic results = JsonConvert.DeserializeObject<dynamic>(YOUR_JSON);
JsonFx can deserialize JSON content into dynamic objects.
Serialize to/from dynamic types (default for .NET 4.0):
var reader = new JsonReader(); var writer = new JsonWriter();
string input = #"{ ""foo"": true, ""array"": [ 42, false, ""Hello!"", null ] }";
dynamic output = reader.Read(input);
Console.WriteLine(output.array[0]); // 42
string json = writer.Write(output);
Console.WriteLine(json); // {"foo":true,"array":[42,false,"Hello!",null]}
Another way using Newtonsoft.Json:
dynamic stuff = Newtonsoft.Json.JsonConvert.DeserializeObject("{ color: 'red', value: 5 }");
string color = stuff.color;
int value = stuff.value;
I came here to find an answer for .NET Core, without any third-party or additional references. It works fine if you use ExpandoObject with the standard JsonSerializer class. Here is the example that worked for me:
using System.Text.Json;
using System.Dynamic;
dynamic json = JsonSerializer.Deserialize<ExpandoObject>(jsonText);
Console.WriteLine(json.name);
This code prints out the string value of a name property that exists within the JSON text passed into the Deserialize method. Voila - no additional libraries, no nothing. Just .NET core.
Edit: May have a problem for several levels of json with nested elements. Worked for a single-level flat object.
I made a new version of the DynamicJsonConverter that uses Expando Objects. I used expando objects, because I wanted to Serialize the dynamic back into JSON using Json.NET.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Web.Script.Serialization;
public static class DynamicJson
{
public static dynamic Parse(string json)
{
JavaScriptSerializer jss = new JavaScriptSerializer();
jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });
dynamic glossaryEntry = jss.Deserialize(json, typeof(object)) as dynamic;
return glossaryEntry;
}
class DynamicJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
var result = ToExpando(dictionary);
return type == typeof(object) ? result : null;
}
private static ExpandoObject ToExpando(IDictionary<string, object> dictionary)
{
var result = new ExpandoObject();
var dic = result as IDictionary<String, object>;
foreach (var item in dictionary)
{
var valueAsDic = item.Value as IDictionary<string, object>;
if (valueAsDic != null)
{
dic.Add(item.Key, ToExpando(valueAsDic));
continue;
}
var arrayList = item.Value as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
dic.Add(item.Key, ToExpando(arrayList));
continue;
}
dic.Add(item.Key, item.Value);
}
return result;
}
private static ArrayList ToExpando(ArrayList obj)
{
ArrayList result = new ArrayList();
foreach (var item in obj)
{
var valueAsDic = item as IDictionary<string, object>;
if (valueAsDic != null)
{
result.Add(ToExpando(valueAsDic));
continue;
}
var arrayList = item as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
result.Add(ToExpando(arrayList));
continue;
}
result.Add(item);
}
return result;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
}
}
}
Creating dynamic objects with Newtonsoft.Json works really great.
//json is your string containing the JSON value
dynamic data = JsonConvert.DeserializeObject<dynamic>(json);
Now you can access the data object just like if it was a regular object. This is the JSON object we currently have as an example:
{ "ID":123,"Name":"Jack","Numbers":[1, 2, 3] }
This is how you access it after deserialization:
data.ID //Retrieve the int
data.Name //Retrieve the string
data.Numbers[0] //Retrieve the first element in the array
I use http://json2csharp.com/ to get a class representing the JSON object.
Input:
{
"name":"John",
"age":31,
"city":"New York",
"Childs":[
{
"name":"Jim",
"age":11
},
{
"name":"Tim",
"age":9
}
]
}
Output:
public class Child
{
public string name { get; set; }
public int age { get; set; }
}
public class Person
{
public string name { get; set; }
public int age { get; set; }
public string city { get; set; }
public List<Child> Childs { get; set; }
}
After that I use Newtonsoft.Json to fill the class:
using Newtonsoft.Json;
namespace GitRepositoryCreator.Common
{
class JObjects
{
public static string Get(object p_object)
{
return JsonConvert.SerializeObject(p_object);
}
internal static T Get<T>(string p_object)
{
return JsonConvert.DeserializeObject<T>(p_object);
}
}
}
You can call it like this:
Person jsonClass = JObjects.Get<Person>(stringJson);
string stringJson = JObjects.Get(jsonClass);
PS:
If your JSON variable name is not a valid C# name (name starts with $) you can fix that like this:
public class Exception
{
[JsonProperty(PropertyName = "$id")]
public string id { get; set; }
public object innerException { get; set; }
public string message { get; set; }
public string typeName { get; set; }
public string typeKey { get; set; }
public int errorCode { get; set; }
public int eventId { get; set; }
}
The simplest way is:
Just include this DLL file.
Use the code like this:
dynamic json = new JDynamic("{a:'abc'}");
// json.a is a string "abc"
dynamic json = new JDynamic("{a:3.1416}");
// json.a is 3.1416m
dynamic json = new JDynamic("{a:1}");
// json.a is
dynamic json = new JDynamic("[1,2,3]");
/json.Length/json.Count is 3
// And you can use json[0]/ json[2] to get the elements
dynamic json = new JDynamic("{a:[1,2,3]}");
//json.a.Length /json.a.Count is 3.
// And you can use json.a[0]/ json.a[2] to get the elements
dynamic json = new JDynamic("[{b:1},{c:1}]");
// json.Length/json.Count is 2.
// And you can use the json[0].b/json[1].c to get the num.
Another option is to "Paste JSON as classes" so it can be deserialised quick and easy.
Simply copy your entire JSON
In Visual Studio: Click Edit → Paste Special → Paste JSON as classes
Here is a better explanation n piccas... ‘Paste JSON As Classes’ in ASP.NET and Web Tools 2012.2 RC
You can extend the JavaScriptSerializer to recursively copy the dictionary it created to expando object(s) and then use them dynamically:
static class JavaScriptSerializerExtensions
{
public static dynamic DeserializeDynamic(this JavaScriptSerializer serializer, string value)
{
var dictionary = serializer.Deserialize<IDictionary<string, object>>(value);
return GetExpando(dictionary);
}
private static ExpandoObject GetExpando(IDictionary<string, object> dictionary)
{
var expando = (IDictionary<string, object>)new ExpandoObject();
foreach (var item in dictionary)
{
var innerDictionary = item.Value as IDictionary<string, object>;
if (innerDictionary != null)
{
expando.Add(item.Key, GetExpando(innerDictionary));
}
else
{
expando.Add(item.Key, item.Value);
}
}
return (ExpandoObject)expando;
}
}
Then you just need to having a using statement for the namespace you defined the extension in (consider just defining them in System.Web.Script.Serialization... another trick is to not use a namespace, then you don't need the using statement at all) and you can consume them like so:
var serializer = new JavaScriptSerializer();
var value = serializer.DeserializeDynamic("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
var name = (string)value.Name; // Jon Smith
var age = (int)value.Age; // 42
var address = value.Address;
var city = (string)address.City; // New York
var state = (string)address.State; // NY
You can use using Newtonsoft.Json
var jRoot =
JsonConvert.DeserializeObject<dynamic>(Encoding.UTF8.GetString(resolvedEvent.Event.Data));
resolvedEvent.Event.Data is my response getting from calling core Event .
Try this:
var units = new { Name = "Phone", Color= "White" };
var jsonResponse = JsonConvert.DeserializeAnonymousType(json, units);
I am using like this in my code and it's working fine
using System.Web.Script.Serialization;
JavaScriptSerializer oJS = new JavaScriptSerializer();
RootObject oRootObject = new RootObject();
oRootObject = oJS.Deserialize<RootObject>(Your JSon String);
Look at the article I wrote on CodeProject, one that answers the question precisely:
Dynamic types with JSON.NET
There is way too much for re-posting it all here, and even less point since that article has an attachment with the key/required source file.
For that I would use JSON.NET to do the low-level parsing of the JSON stream and then build up the object hierarchy out of instances of the ExpandoObject class.
To get an ExpandoObject:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
Container container = JsonConvert.Deserialize<Container>(jsonAsString, new ExpandoObjectConverter());
Deserializing in JSON.NET can be dynamic using the JObject class, which is included in that library. My JSON string represents these classes:
public class Foo {
public int Age {get;set;}
public Bar Bar {get;set;}
}
public class Bar {
public DateTime BDay {get;set;}
}
Now we deserialize the string WITHOUT referencing the above classes:
var dyn = JsonConvert.DeserializeObject<JObject>(jsonAsFooString);
JProperty propAge = dyn.Properties().FirstOrDefault(i=>i.Name == "Age");
if(propAge != null) {
int age = int.Parse(propAge.Value.ToString());
Console.WriteLine("age=" + age);
}
//or as a one-liner:
int myage = int.Parse(dyn.Properties().First(i=>i.Name == "Age").Value.ToString());
Or if you want to go deeper:
var propBar = dyn.Properties().FirstOrDefault(i=>i.Name == "Bar");
if(propBar != null) {
JObject o = (JObject)propBar.First();
var propBDay = o.Properties().FirstOrDefault (i => i.Name=="BDay");
if(propBDay != null) {
DateTime bday = DateTime.Parse(propBDay.Value.ToString());
Console.WriteLine("birthday=" + bday.ToString("MM/dd/yyyy"));
}
}
//or as a one-liner:
DateTime mybday = DateTime.Parse(((JObject)dyn.Properties().First(i=>i.Name == "Bar").First()).Properties().First(i=>i.Name == "BDay").Value.ToString());
See post for a complete example.
The object you want DynamicJSONObject is included in the System.Web.Helpers.dll from the ASP.NET Web Pages package, which is part of WebMatrix.
There is a lightweight JSON library for C# called SimpleJson.
It supports .NET 3.5+, Silverlight and Windows Phone 7.
It supports dynamic for .NET 4.0
It can also be installed as a NuGet package
Install-Package SimpleJson
Use DataSet(C#) with JavaScript. A simple function for creating a JSON stream with DataSet input. Create JSON content like (multi table dataset):
[[{a:1,b:2,c:3},{a:3,b:5,c:6}],[{a:23,b:45,c:35},{a:58,b:59,c:45}]]
Just client side, use eval. For example,
var d = eval('[[{a:1,b:2,c:3},{a:3,b:5,c:6}],[{a:23,b:45,c:35},{a:58,b:59,c:45}]]')
Then use:
d[0][0].a // out 1 from table 0 row 0
d[1][1].b // out 59 from table 1 row 1
// Created by Behnam Mohammadi And Saeed Ahmadian
public string jsonMini(DataSet ds)
{
int t = 0, r = 0, c = 0;
string stream = "[";
for (t = 0; t < ds.Tables.Count; t++)
{
stream += "[";
for (r = 0; r < ds.Tables[t].Rows.Count; r++)
{
stream += "{";
for (c = 0; c < ds.Tables[t].Columns.Count; c++)
{
stream += ds.Tables[t].Columns[c].ToString() + ":'" +
ds.Tables[t].Rows[r][c].ToString() + "',";
}
if (c>0)
stream = stream.Substring(0, stream.Length - 1);
stream += "},";
}
if (r>0)
stream = stream.Substring(0, stream.Length - 1);
stream += "],";
}
if (t>0)
stream = stream.Substring(0, stream.Length - 1);
stream += "];";
return stream;
}
How to parse easy JSON content with dynamic & JavaScriptSerializer
Please add reference of System.Web.Extensions and add this namespace using System.Web.Script.Serialization; at top:
public static void EasyJson()
{
var jsonText = #"{
""some_number"": 108.541,
""date_time"": ""2011-04-13T15:34:09Z"",
""serial_number"": ""SN1234""
}";
var jss = new JavaScriptSerializer();
var dict = jss.Deserialize<dynamic>(jsonText);
Console.WriteLine(dict["some_number"]);
Console.ReadLine();
}
How to parse nested & complex json with dynamic & JavaScriptSerializer
Please add reference of System.Web.Extensions and add this namespace using System.Web.Script.Serialization; at top:
public static void ComplexJson()
{
var jsonText = #"{
""some_number"": 108.541,
""date_time"": ""2011-04-13T15:34:09Z"",
""serial_number"": ""SN1234"",
""more_data"": {
""field1"": 1.0,
""field2"": ""hello""
}
}";
var jss = new JavaScriptSerializer();
var dict = jss.Deserialize<dynamic>(jsonText);
Console.WriteLine(dict["some_number"]);
Console.WriteLine(dict["more_data"]["field2"]);
Console.ReadLine();
}
I want to do this programmatically in unit tests, I do have the luxury of typing it out.
My solution is:
var dict = JsonConvert.DeserializeObject<ExpandoObject>(json) as IDictionary<string, object>;
Now I can assert that
dict.ContainsKey("ExpectedProperty");
With Cinchoo ETL - an open source library available to parse JSON into a dynamic object:
string json = #"{
""key1"": [
{
""action"": ""open"",
""timestamp"": ""2018-09-05 20:46:00"",
""url"": null,
""ip"": ""66.102.6.98""
}
]
}";
using (var p = ChoJSONReader.LoadText(json)
.WithJSONPath("$..key1")
)
{
foreach (var rec in p)
{
Console.WriteLine("Action: " + rec.action);
Console.WriteLine("Timestamp: " + rec.timestamp);
Console.WriteLine("URL: " + rec.url);
Console.WriteLine("IP address: " + rec.ip);
}
}
Output:
Action: open
Timestamp: 2018-09-05 20:46:00
URL: http://www.google.com
IP address: 66.102.6.98
Sample fiddle: https://dotnetfiddle.net/S0ehSV
For more information, please visit codeproject articles
Disclaimer: I'm the author of this library.
try this way!
JSON example:
[{
"id": 140,
"group": 1,
"text": "xxx",
"creation_date": 123456,
"created_by": "xxx#gmail.co",
"tags": ["xxxxx"]
}, {
"id": 141,
"group": 1,
"text": "xxxx",
"creation_date": 123456,
"created_by": "xxx#gmail.com",
"tags": ["xxxxx"]
}]
C# code:
var jsonString = (File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(),"delete_result.json")));
var objects = JsonConvert.DeserializeObject<dynamic>(jsonString);
foreach(var o in objects)
{
Console.WriteLine($"{o.id.ToString()}");
}
I really like System.Web.Helpers,
dynamic data = Json.Decode(json);
as it supports usage like
var val = data.Members.NumberTen;
or
var val data.Members["10"];
The reference to System.Web.Helpers.DLL is really crazy, it is not even console and desktop app friendly. Here is my attempt to extract the same functionalities as a standalone file directly from https://github.com/mono/aspnetwebstack/tree/master/src/System.Web.Helpers
(Share this as for education purpose only)
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using Microsoft.CSharp.RuntimeBinder;
using System.Web.Script.Serialization;
using System.IO;
using System.Collections;
using System.Linq;
using System.Globalization;
namespace System.Web.Helpers
{
public static class Json
{
private static readonly JavaScriptSerializer _serializer = CreateSerializer();
public static string Encode(object value)
{
// Serialize our dynamic array type as an array
DynamicJsonArray jsonArray = value as DynamicJsonArray;
if (jsonArray != null)
{
return _serializer.Serialize((object[])jsonArray);
}
return _serializer.Serialize(value);
}
public static void Write(object value, TextWriter writer)
{
writer.Write(_serializer.Serialize(value));
}
public static dynamic Decode(string value)
{
return WrapObject(_serializer.DeserializeObject(value));
}
public static dynamic Decode(string value, Type targetType)
{
return WrapObject(_serializer.Deserialize(value, targetType));
}
public static T Decode<T>(string value)
{
return _serializer.Deserialize<T>(value);
}
private static JavaScriptSerializer CreateSerializer()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJavaScriptConverter() });
return serializer;
}
internal class DynamicJavaScriptConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get
{
yield return typeof(IDynamicMetaObjectProvider);
yield return typeof(DynamicObject);
}
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotSupportedException();
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
IEnumerable<string> memberNames = DynamicHelper.GetMemberNames(obj);
foreach (string item in memberNames)
{
dictionary[item] = DynamicHelper.GetMemberValue(obj, item);
}
return dictionary;
}
}
internal static dynamic WrapObject(object value)
{
// The JavaScriptSerializer returns IDictionary<string, object> for objects
// and object[] for arrays, so we wrap those in different dynamic objects
// so we can access the object graph using dynamic
var dictionaryValues = value as IDictionary<string, object>;
if (dictionaryValues != null)
{
return new DynamicJsonObject(dictionaryValues);
}
var arrayValues = value as object[];
if (arrayValues != null)
{
return new DynamicJsonArray(arrayValues);
}
return value;
}
}
// REVIEW: Consider implementing ICustomTypeDescriptor and IDictionary<string, object>
public class DynamicJsonObject : DynamicObject
{
private readonly IDictionary<string, object> _values;
public DynamicJsonObject(IDictionary<string, object> values)
{
Debug.Assert(values != null);
_values = values.ToDictionary(p => p.Key, p => Json.WrapObject(p.Value),
StringComparer.OrdinalIgnoreCase);
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
result = null;
if (binder.Type.IsAssignableFrom(_values.GetType()))
{
result = _values;
}
else
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "HelpersResources.Json_UnableToConvertType", binder.Type));
}
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = GetValue(binder.Name);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_values[binder.Name] = Json.WrapObject(value);
return true;
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
string key = GetKey(indexes);
if (!String.IsNullOrEmpty(key))
{
_values[key] = Json.WrapObject(value);
}
return true;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
string key = GetKey(indexes);
result = null;
if (!String.IsNullOrEmpty(key))
{
result = GetValue(key);
}
return true;
}
private static string GetKey(object[] indexes)
{
if (indexes.Length == 1)
{
return (string)indexes[0];
}
// REVIEW: Should this throw?
return null;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return _values.Keys;
}
private object GetValue(string name)
{
object result;
if (_values.TryGetValue(name, out result))
{
return result;
}
return null;
}
}
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "This class isn't meant to be used directly")]
public class DynamicJsonArray : DynamicObject, IEnumerable<object>
{
private readonly object[] _arrayValues;
public DynamicJsonArray(object[] arrayValues)
{
Debug.Assert(arrayValues != null);
_arrayValues = arrayValues.Select(Json.WrapObject).ToArray();
}
public int Length
{
get { return _arrayValues.Length; }
}
public dynamic this[int index]
{
get { return _arrayValues[index]; }
set { _arrayValues[index] = Json.WrapObject(value); }
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (_arrayValues.GetType().IsAssignableFrom(binder.Type))
{
result = _arrayValues;
return true;
}
return base.TryConvert(binder, out result);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
// Testing for members should never throw. This is important when dealing with
// services that return different json results. Testing for a member shouldn't throw,
// it should just return null (or undefined)
result = null;
return true;
}
public IEnumerator GetEnumerator()
{
return _arrayValues.GetEnumerator();
}
private IEnumerable<object> GetEnumerable()
{
return _arrayValues.AsEnumerable();
}
IEnumerator<object> IEnumerable<object>.GetEnumerator()
{
return GetEnumerable().GetEnumerator();
}
[SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "This class isn't meant to be used directly")]
public static implicit operator object[](DynamicJsonArray obj)
{
return obj._arrayValues;
}
[SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "This class isn't meant to be used directly")]
public static implicit operator Array(DynamicJsonArray obj)
{
return obj._arrayValues;
}
}
/// <summary>
/// Helper to evaluate different method on dynamic objects
/// </summary>
public static class DynamicHelper
{
// We must pass in "object" instead of "dynamic" for the target dynamic object because if we use dynamic, the compiler will
// convert the call to this helper into a dynamic expression, even though we don't need it to be. Since this class is internal,
// it cannot be accessed from a dynamic expression and thus we get errors.
// Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
public static bool TryGetMemberValue(object obj, string memberName, out object result)
{
try
{
result = GetMemberValue(obj, memberName);
return true;
}
catch (RuntimeBinderException)
{
}
catch (RuntimeBinderInternalCompilerException)
{
}
// We catch the C# specific runtime binder exceptions since we're using the C# binder in this case
result = null;
return false;
}
// Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to swallow exceptions that happen during runtime binding")]
public static bool TryGetMemberValue(object obj, GetMemberBinder binder, out object result)
{
try
{
// VB us an instance of GetBinderAdapter that does not implement FallbackGetMemeber. This causes lookup of property expressions on dynamic objects to fail.
// Since all types are private to the assembly, we assume that as long as they belong to CSharp runtime, it is the right one.
if (typeof(Binder).Assembly.Equals(binder.GetType().Assembly))
{
// Only use the binder if its a C# binder.
result = GetMemberValue(obj, binder);
}
else
{
result = GetMemberValue(obj, binder.Name);
}
return true;
}
catch
{
result = null;
return false;
}
}
// Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
public static object GetMemberValue(object obj, string memberName)
{
var callSite = GetMemberAccessCallSite(memberName);
return callSite.Target(callSite, obj);
}
// Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
public static object GetMemberValue(object obj, GetMemberBinder binder)
{
var callSite = GetMemberAccessCallSite(binder);
return callSite.Target(callSite, obj);
}
// dynamic d = new object();
// object s = d.Name;
// The following code gets generated for this expression:
// callSite = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "Name", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
// callSite.Target(callSite, d);
// typeof(Program) is the containing type of the dynamic operation.
// Dev10 Bug 914027 - Changed the callsite's target parameter from dynamic to object, see comment at top for details
public static CallSite<Func<CallSite, object, object>> GetMemberAccessCallSite(string memberName)
{
var binder = Binder.GetMember(CSharpBinderFlags.None, memberName, typeof(DynamicHelper), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
return GetMemberAccessCallSite(binder);
}
// Dev10 Bug 914027 - Changed the callsite's target parameter from dynamic to object, see comment at top for details
public static CallSite<Func<CallSite, object, object>> GetMemberAccessCallSite(CallSiteBinder binder)
{
return CallSite<Func<CallSite, object, object>>.Create(binder);
}
// Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
public static IEnumerable<string> GetMemberNames(object obj)
{
var provider = obj as IDynamicMetaObjectProvider;
Debug.Assert(provider != null, "obj doesn't implement IDynamicMetaObjectProvider");
Expression parameter = Expression.Parameter(typeof(object));
return provider.GetMetaObject(parameter).GetDynamicMemberNames();
}
}
}

Sort a list of dot notated namespace strings into a nested JSON string c#

How can I sort a list of dot notated namespace strings into a nested JSON string?
something like this.
var string1 = "example.string.1";
var string2 = "example.string.2";
var string3 = "example.anotherstring.1";
sorted into this.
{
"example": {
"string": ["1", "2"],
"anotherstring": ["1"]
}
}
EDIT 1
Thanks guys! I was able to get it CLOSE to working using your answers.
with this list it works:
var strings = new List<string>
{
"example.string.1",
"example.string.2",
"example.anotherstring.1",
};
public void Example()
{
var dict = new Dictionary<string, dynamic>();
foreach (var s in strings)
{
AddPartsToDictionary(dict, s.Split('.'));
}
}
public void AddPartsToDictionary(IDictionary<string, dynamic> dict, string[] parts)
{
for (var i = 0; i < parts.Length; i++)
{
if (i < parts.Length - 2)
{
if (!dict.ContainsKey(parts[i]))
dict.Add(parts[i], new Dictionary<string, dynamic>());
dict = dict[parts[i]];
}
else if (i < parts.Length - 1)
{
if (!dict.ContainsKey(parts[i]))
{
var list = new List<string>();
dict[parts[i]] = list;
}
}
else
{
var list = dict[parts[i - 1]] as List<string>;
list.Add(parts[i]);
}
}
}
result json
{
"example": {
"string": [
"1",
"2"
],
"anotherstring": [
"1"
]
}
}
with this list it fails
var strings = new List<string>
{
"example.string.1",
"example.string.example.1",
"example.string.2",
"example.anotherstring.1",
"example.string.example.2",
"string.example.2"
};
I might have to sort the list or something, still working on it. Hope this helps with figuring it out.
Objects All the Way Down
If using objects (instead of arrays) all the time is okay, then this will work.
var strings = new List<string> {
"example.string.1",
"example.string.2",
"example.anotherstring.1",
};
var result = strings.Aggregate(new Dictionary<string, Object>(), (acc, s) =>
{
var level = acc;
foreach(var segment in s.Split('.'))
{
if (!level.ContainsKey(segment))
{
var child = new Dictionary<string, Object>();
level.Add(segment, child);
}
level = level[segment] as Dictionary<string, Object>;
}
return acc;
});
var json = JsonConvert.SerializeObject(result, Formatting.Indented);
The output has objects all the way down.
{
"example": {
"string": {
"1": {},
"2": {}
},
"anotherstring": {
"1": {}
}
}
}
Arrays at the Last Level (with a Fiddle)
If you would like arrays at the last level, then we need a precise definition of what defines the last level. For instance, does the last level always contain only integers? If so the following works.
var result = strings.Aggregate(new Dictionary<string, Object>(), (acc, s) =>
{
Dictionary<string, Object> previousLevel = null;
Dictionary<string, Object> nextLevel = acc;
string previousSegment = null;
foreach (string nextSegment in s.Split('.'))
{
if (Int16.TryParse(nextSegment, out _))
{
if (previousLevel[previousSegment] is Dictionary<string, Object>)
{
previousLevel[previousSegment] = new List<string>();
}
var list = previousLevel[previousSegment] as List<string>;
list.Add(nextSegment);
}
else
{
if (!nextLevel.ContainsKey(nextSegment))
{
var child = new Dictionary<string, Object>();
nextLevel.Add(nextSegment, child);
}
previousSegment = nextSegment;
previousLevel = nextLevel;
nextLevel = nextLevel[nextSegment] as Dictionary<string, Object>;
}
}
return acc;
});
The output has arrays at the last level.
{
"example": {
"string": [
"1",
"2"
],
"anotherstring": [
"1",
"2"
]
}
}
This array version will work only if the last level is only of integer types, which is what your original question required.
The more difficult case is to handle mixed string and integer types at the same level, which would have an input that looks something like this:
"System.1",
"System.2",
"System.Collections.1",
"System.Collections.2",
"System.Collections.Generic.1"
That requires a more complex algorithm. If that is the hidden requirement, consider asking a brand new follow-up question and linking to it in a comment to this answer.
I thought id have a quick go at this, its close but not exactly what you wanted. However i thought it might inspire you or someone else to have a try.
Disclaimer, i'm not really a Json.net aficionado and this code is
fairly well.. unusual, to say the least
Demo Here
Given a list
var list = new List<string>
{
"example.string.1",
"example.string.2",
"example.anotherstring.1",
"example.anotherstring.2",
"System",
"System.Diagnostics",
"System.Text",
"System.Collections.Generic",
"System.Linq",
"System.Diagnostics.1",
"System.Text.1",
"System.Collections.Generic.1",
"System.Linq.1",
"System.Diagnostics.2",
"System.Text.2",
"System.Collections.Generic.2",
"System.Linq.2"
};
Some Classes
public class RawValue
{
public RawValue(string key, IEnumerable<IEnumerable<string>> values)
{
Key = key;
Values = values;
}
public string Key { get; set; }
public IEnumerable<IEnumerable<string>> Values { get; set; }
}
public class NameSpace : Dictionary<string, NameSpace>
{
}
Some Helpers
private static IEnumerable<RawValue> GetRawValues(RawValue value)
{
return value.Values.Where(x => x.Any())
.GroupBy(x => x.FirstOrDefault())
.Select(x => new RawValue(x.Key, x.Select(y => y.Skip(1))));
}
private static NameSpace GroupNameSpaces(IEnumerable<RawValue> groups)
{
var result = new NameSpace();
foreach (var group in groups)
result.Add(group.Key, GroupNameSpaces(GetRawValues(group)));
return result;
}
Main Code
var groups = list.Select(x => x.Split('.'))
.GroupBy(x => x.FirstOrDefault())
.Select(x => new RawValue(x.Key, x.Select(y => y.Skip(1))));
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
var result = GroupNameSpaces(groups);
var json = JsonConvert.SerializeObject(result, Formatting.Indented, settings);
Console.WriteLine(json);
Output
{
"example": {
"string": {
"1": {},
"2": {}
},
"anotherstring": {
"1": {},
"2": {}
}
},
"System": {
"Diagnostics": {
"1": {},
"2": {}
},
"Text": {
"1": {},
"2": {}
},
"Collections": {
"Generic": {
"1": {},
"2": {}
}
},
"Linq": {
"1": {},
"2": {}
}
}
}
I think my downfall was having to use NameSpace : Dictionary<string, NameSpace> due to the fact i didn't know how to better serialise in Json.Net. Also without spending time on this, i couldnt figure out how to convert the last node to an json array. though i'm not sure if you will be able to if you think about it. However i'm sure this can be done better

Custom JSON.net deserialization into a Dictionary<string, string>

I am looking to use JSON.net to deserialize a JSON structure into a Dictionary. The trick is that the JSON document is a hierarchy of nested objects but I'd like to only look at the top-level property+value pairs.
Eg.
{
"prop1": 142,
"prop2": "Some description",
"object_prop": {
"abc": 2,
"def": {
"foo": "hello",
"bar": 4
}
}
}
Based on the above example, I'd like to have my deserialized dictionary have 3 items in it: "prop1", "prop2", and "object_prop". "object_prop" should just be a string (which I will deserialize to an object at some later point in time.
Note: I'm looking to do this because I want to create a re-usable library that just knows about top-level key/value pairs and where clients consuming the library can define the type of the values at a later point in time. (ie. I don't want my re-usable library bound to the object types ... namely "object_prop").
How about something like this?
class Program
{
static void Main(string[] args)
{
string json = #"
{
""prop1"": 142,
""prop2"": ""Some description"",
""object_prop"": {
""abc"": 2,
""def"": {
""foo"": ""hello"",
""bar"": 4
}
},
""prop3"": 3.14
}";
Dictionary<string, string> dict = new Dictionary<string, string>();
JObject jo = JObject.Parse(json);
foreach (JProperty prop in jo.Properties())
{
if (prop.Value.Type == JTokenType.Array ||
prop.Value.Type == JTokenType.Object)
{
// JSON string for complex object
dict.Add(prop.Name, prop.Value.ToString(Formatting.None));
}
else
{
// primitive value converted to string
object value = ((JValue)prop.Value).Value;
dict.Add(prop.Name, value != null ? value.ToString() : null);
}
}
foreach (KeyValuePair<string, string> kvp in dict)
{
Console.WriteLine(kvp.Key + " = " + kvp.Value);
}
}
}
Output:
prop1 = 142
prop2 = Some description
object_prop = {"abc":2,"def":{"foo":"hello","bar":4}}
prop3 = 3.14

Categories

Resources