I have a web API method that accepts an arbitrary json payload into a JObject property. As such I don't know what's coming but I still need to translate it to .NET types. I would like to have a Dictionary<string,object> so that I can deal with it any way I want to.
I have searched a lot, but couldn't find anything and ended up starting a messy method to do this conversion, key by key, value by value. Is there an easy way to do it?
Input ->
JObject person = new JObject(
new JProperty("Name", "John Smith"),
new JProperty("BirthDate", new DateTime(1983, 3, 20)),
new JProperty("Hobbies", new JArray("Play football", "Programming")),
new JProperty("Extra", new JObject(
new JProperty("Foo", 1),
new JProperty("Bar", new JArray(1, 2, 3))
)
)
Thanks!
If you have JObject objects, the following might work:
JObject person;
var values = person.ToObject<Dictionary<string, object>>();
If you do not have a JObject you can create one with the Newtonsoft.Json.Linq extension method:
using Newtonsoft.Json.Linq;
var values = JObject.FromObject(person).ToObject<Dictionary<string, object>>();
Otherwise, this answer might point you in the right direction, as it deserializes a JSON string to a Dictionary.
var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
I ended up using a mix of both answers as none really nailed it.
ToObject() can do the first level of properties in a JSON object, but nested objects won't be converted to Dictionary().
There's also no need to do everything manually as ToObject() is pretty good with first level properties.
Here is the code:
public static class JObjectExtensions
{
public static IDictionary<string, object> ToDictionary(this JObject #object)
{
var result = #object.ToObject<Dictionary<string, object>>();
var JObjectKeys = (from r in result
let key = r.Key
let value = r.Value
where value.GetType() == typeof(JObject)
select key).ToList();
var JArrayKeys = (from r in result
let key = r.Key
let value = r.Value
where value.GetType() == typeof(JArray)
select key).ToList();
JArrayKeys.ForEach(key => result[key] = ((JArray)result[key]).Values().Select(x => ((JValue)x).Value).ToArray());
JObjectKeys.ForEach(key => result[key] = ToDictionary(result[key] as JObject));
return result;
}
}
It might have edge cases where it won't work and the performance is not the strongest quality of it.
Thanks guys!
I've modified the code to recurse JArrays an JObjects nested in JArrays/JObjects, which the accepted answer does not, as pointed out by #Nawaz.
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
public static class JsonConversionExtensions
{
public static IDictionary<string, object> ToDictionary(this JObject json)
{
var propertyValuePairs = json.ToObject<Dictionary<string, object>>();
ProcessJObjectProperties(propertyValuePairs);
ProcessJArrayProperties(propertyValuePairs);
return propertyValuePairs;
}
private static void ProcessJObjectProperties(IDictionary<string, object> propertyValuePairs)
{
var objectPropertyNames = (from property in propertyValuePairs
let propertyName = property.Key
let value = property.Value
where value is JObject
select propertyName).ToList();
objectPropertyNames.ForEach(propertyName => propertyValuePairs[propertyName] = ToDictionary((JObject) propertyValuePairs[propertyName]));
}
private static void ProcessJArrayProperties(IDictionary<string, object> propertyValuePairs)
{
var arrayPropertyNames = (from property in propertyValuePairs
let propertyName = property.Key
let value = property.Value
where value is JArray
select propertyName).ToList();
arrayPropertyNames.ForEach(propertyName => propertyValuePairs[propertyName] = ToArray((JArray) propertyValuePairs[propertyName]));
}
public static object[] ToArray(this JArray array)
{
return array.ToObject<object[]>().Select(ProcessArrayEntry).ToArray();
}
private static object ProcessArrayEntry(object value)
{
if (value is JObject)
{
return ToDictionary((JObject) value);
}
if (value is JArray)
{
return ToArray((JArray) value);
}
return value;
}
}
Here is a simpler version:
public static object ToCollections(object o)
{
var jo = o as JObject;
if (jo != null) return jo.ToObject<IDictionary<string, object>>().ToDictionary(k => k.Key, v => ToCollections(v.Value));
var ja = o as JArray;
if (ja != null) return ja.ToObject<List<object>>().Select(ToCollections).ToList();
return o;
}
If using C# 7 we can use pattern matching where it would look like this:
public static object ToCollections(object o)
{
if (o is JObject jo) return jo.ToObject<IDictionary<string, object>>().ToDictionary(k => k.Key, v => ToCollections(v.Value));
if (o is JArray ja) return ja.ToObject<List<object>>().Select(ToCollections).ToList();
return o;
}
Sounds like a good use case for extension methods - I had something lying around that was pretty straightforward to convert to Json.NET (Thanks NuGet!):
Of course, this is quickly hacked together - you'd want to clean it up, etc.
public static class JTokenExt
{
public static Dictionary<string, object>
Bagify(this JToken obj, string name = null)
{
name = name ?? "obj";
if(obj is JObject)
{
var asBag =
from prop in (obj as JObject).Properties()
let propName = prop.Name
let propValue = prop.Value is JValue
? new Dictionary<string,object>()
{
{prop.Name, prop.Value}
}
: prop.Value.Bagify(prop.Name)
select new KeyValuePair<string, object>(propName, propValue);
return asBag.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
if(obj is JArray)
{
var vals = (obj as JArray).Values();
var alldicts = vals
.SelectMany(val => val.Bagify(name))
.Select(x => x.Value)
.ToArray();
return new Dictionary<string,object>()
{
{name, (object)alldicts}
};
}
if(obj is JValue)
{
return new Dictionary<string,object>()
{
{name, (obj as JValue)}
};
}
return new Dictionary<string,object>()
{
{name, null}
};
}
}
Related
I have two json objects and I compare them, But it is compared by key and value,
And I want to compare two json objects by only key,
How do I it?
This my code:
var jdp = new JsonDiffPatch();
var areEqual2 = jdp.Diff(json1, json2);
you can create and use a class like this:
class CustomComparer : IEqualityComparer<YourObjectType>
{
public bool Equals(YourObjectType first, YourObjectType second)
{
if (first == null | second == null) { return false; }
else if (first.Hash == second.Hash)
return true;
else return false;
}
public int GetHashCode(YourObjectType obj)
{
throw new NotImplementedException();
}
}
If you want to get a different between 2 json:
private List<string> GetDiff(List<string> path1, List<string> path2)
{
List<string> equal=new List<string>();
foreach (var j1 in path1)
{
foreach (var j2 in path2)
{
if (j1 == j2)
{
equal.Add(j1);
}
}
}
return equal;
}
One way you could achive this is by retrieving the names/path of all keys in json and comparing the List. For example,
var path1 = GetAllPaths(json1).OrderBy(x=>x).ToList();
var path2 = GetAllPaths(json2).OrderBy(x=>x).ToList();
var result = path1.SequenceEqual(path2);
Where GetAllPaths is defined as
private IEnumerable<string> GetAllPaths(string json)
{
var regex = new Regex(#"\[\d*\].",RegexOptions.Compiled);
return JObject.Parse(json).DescendantsAndSelf()
.OfType<JProperty>()
.Where(jp => jp.Value is JValue)
.Select(jp => regex.Replace(jp.Path,".")).Distinct();
}
Sample Demo
I am new to Reflection so please excuse my noob question. How can I create a Method that takes two Parameters, a Generic List and a String and then finds all items in that List where any property value matches the string.
So for example we have an object with 3 properties, I pass a list of this object to the method and a search string and it returns back a list of objects where any of the properties may contain the search string.
I can do like this :
var temp = list.AsQueryable().Where("SomeField == 1").Select("it");
But how can I make this method Generic so I can pass any List of Objects to it ?
Thanks in advance...
If you are using Dynamic Linq, try this
public static IEnumerable<T> Filter<T>(IEnumerable<T> source, string searchStr)
{
var propsToCheck = typeof (T).GetProperties().Where(a => a.PropertyType == typeof(string));
var filter = propsToCheck.Aggregate(string.Empty, (s, p) => (s == string.Empty ? string.Empty : string.Format("{0} OR ", s)) + string.Format("{0} == #0", p.Name));
var filtered = source.AsQueryable().Where(filter, searchStr);
return filtered;
}
Use Type.GetProperties() to get all the properties of an object. Use PropertyInfo.GetValue() to get the value of a given property in a given object. You need to figure out how you want a match your string to a DateTime, to numbers, or to other complex objects. Put it all into a function like bool IsMatch(this object obj, string val). Then you can filter your list like list.Where(x => x.IsMatch("something")).
Here you go mate:
private static void Main(string[] args)
{
var list = new List<object> {new {prop1 = "A", prop2 = "B"},new {prop3 = "B", prop4 = "C"}};
var subList = SearchForStringInProperties(list, "C");
}
private static IEnumerable<object> SearchForStringInProperties(IEnumerable<object> list, string searchString)
{
return from obj in list where FindStringInObjProperties(obj, searchString) select obj;
}
private static bool FindStringInObjProperties(object obj, string searchString)
{
return obj.GetType().GetProperties().Any(property => obj.GetType().GetProperty(property.Name).GetValue(obj).ToString().Equals(searchString));
}
If you just want to match the properties with same type as your argument, this extension method can help,
public static class ListExtensions
{
public static IEnumerable<T> MatchWithAnyProperty<T, TK>(this IEnumerable<T> list, TK value)
{
var argType = typeof (TK);
var properties = typeof(T).GetProperties().Where(x => x.PropertyType.IsAssignableFrom(argType));
return list.Where(item => properties.Any(prop =>
{
var propertyValue = prop.GetValue(item, null);
if (value == null)
return propertyValue == null;
return propertyValue.Equals(value);
}));
}
}
This can be used like,
var items = new[]
{
new
{
Name = "Test",
Age = 20,
Test=25
},
new
{
Name = "Hello",
Age = 10,
Test=15
},
new
{
Name = "T2gdhest",
Age = 14,
Test=20
},
new
{
Name = "hai",
Age = 33,
Test=10
},
new
{
Name = "why not",
Age = 10,
Test=33
},
};
var match= items.MatchWithAnyProperty(10);
foreach (var item in match)
{
Console.WriteLine(item.Name);
}
Console.ReadKey();
And there is the old way ...
public static IList<T> MyMethod<T>(IList<T> myList, string filter)
{
if (myList == null) return null;
if (filter == null) return myList;
var tfilter = filter.GetType();
var properties = typeof(T).GetProperties().Where(x => x.PropertyType.FullName == typeof(string).FullName);
if (!properties.Any()) return null;
var res = new List<T>();
foreach(var el in myList)
{
foreach(var p in properties)
{
if ((string)p.GetValue(el) == filter)
{
res.Add(el);
break;
}
}
}
return res;
}
How do I convert a dynamic object to a Dictionary<TKey, TValue> in C# What can I do?
public static void MyMethod(object obj)
{
if (typeof(IDictionary).IsAssignableFrom(obj.GetType()))
{
// My object is a dictionary, casting the object:
// (Dictionary<string, string>) obj;
// causes error ...
}
else
{
// My object is not a dictionary
}
}
The above answers are all cool. I found it easy to json serialize the object and deserialize as a dictionary.
var json = JsonConvert.SerializeObject(obj);
var dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
I don't know how performance is effected but this is much easier to read. You could also wrap it inside a function.
public static Dictionary<string, TValue> ToDictionary<TValue>(object obj)
{
var json = JsonConvert.SerializeObject(obj);
var dictionary = JsonConvert.DeserializeObject<Dictionary<string, TValue>>(json);
return dictionary;
}
Use like so:
var obj = new { foo = 12345, boo = true };
var dictionary = ToDictionary<string>(obj);
I use this helper:
public static class ObjectToDictionaryHelper
{
public static IDictionary<string, object> ToDictionary(this object source)
{
return source.ToDictionary<object>();
}
public static IDictionary<string, T> ToDictionary<T>(this object source)
{
if (source == null)
ThrowExceptionWhenSourceArgumentIsNull();
var dictionary = new Dictionary<string, T>();
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source))
AddPropertyToDictionary<T>(property, source, dictionary);
return dictionary;
}
private static void AddPropertyToDictionary<T>(PropertyDescriptor property, object source, Dictionary<string, T> dictionary)
{
object value = property.GetValue(source);
if (IsOfType<T>(value))
dictionary.Add(property.Name, (T)value);
}
private static bool IsOfType<T>(object value)
{
return value is T;
}
private static void ThrowExceptionWhenSourceArgumentIsNull()
{
throw new ArgumentNullException("source", "Unable to convert object to a dictionary. The source object is null.");
}
}
the usage is just to call .ToDictionary() on an object
Hope it helps.
public static KeyValuePair<object, object > Cast<K, V>(this KeyValuePair<K, V> kvp)
{
return new KeyValuePair<object, object>(kvp.Key, kvp.Value);
}
public static KeyValuePair<T, V> CastFrom<T, V>(Object obj)
{
return (KeyValuePair<T, V>) obj;
}
public static KeyValuePair<object , object > CastFrom(Object obj)
{
var type = obj.GetType();
if (type.IsGenericType)
{
if (type == typeof (KeyValuePair<,>))
{
var key = type.GetProperty("Key");
var value = type.GetProperty("Value");
var keyObj = key.GetValue(obj, null);
var valueObj = value.GetValue(obj, null);
return new KeyValuePair<object, object>(keyObj, valueObj);
}
}
throw new ArgumentException(" ### -> public static KeyValuePair<object , object > CastFrom(Object obj) : Error : obj argument must be KeyValuePair<,>");
}
From the OP:
Instead of converting my whole Dictionary, i decided to keep my obj
dynamic the whole time. When i access the keys and values of my
Dictionary with a foreach later, i use foreach(dynamic key in
obj.Keys) and convert the keys and values to strings simply.
Another option is to use NewtonSoft.JSON.
var dictionary = JObject.FromObject(anObject).ToObject<Dictionary<string, object>>();
If you don't mind LINQ Expressions;
public static Dictionary<string, object> ConvertFromObjectToDictionary(object arg)
{
return arg.GetType().GetProperties().ToDictionary(property => property.Name, property => property.GetValue(arg));
}
this should work:
for numbers, strings, date, etc.:
public static void MyMethod(object obj)
{
if (typeof(IDictionary).IsAssignableFrom(obj.GetType()))
{
IDictionary idict = (IDictionary)obj;
Dictionary<string, string> newDict = new Dictionary<string, string>();
foreach (object key in idict.Keys)
{
newDict.Add(key.ToString(), idict[key].ToString());
}
}
else
{
// My object is not a dictionary
}
}
if your dictionary also contains some other objects:
public static void MyMethod(object obj)
{
if (typeof(IDictionary).IsAssignableFrom(obj.GetType()))
{
IDictionary idict = (IDictionary)obj;
Dictionary<string, string> newDict = new Dictionary<string, string>();
foreach (object key in idict.Keys)
{
newDict.Add(objToString(key), objToString(idict[key]));
}
}
else
{
// My object is not a dictionary
}
}
private static string objToString(object obj)
{
string str = "";
if (obj.GetType().FullName == "System.String")
{
str = (string)obj;
}
else if (obj.GetType().FullName == "test.Testclass")
{
TestClass c = (TestClass)obj;
str = c.Info;
}
return str;
}
public static void MyMethod(object obj){
Dictionary<string, string> dicEditdata = data as Dictionary<string, string>;
string abc=dicEditdata["id"].ToString();}
suppose---
if you place the cursor over the object(obj) while debugging and
if you get an object with the value {['id':'ID1003']}
then you can use the value as
string abc=dicEditdata["id"].ToString();
Assuming key can only be a string but value can be anything try this
public static Dictionary<TKey, TValue> MyMethod<TKey, TValue>(object obj)
{
if (obj is Dictionary<TKey, TValue> stringDictionary)
{
return stringDictionary;
}
if (obj is IDictionary baseDictionary)
{
var dictionary = new Dictionary<TKey, TValue>();
foreach (DictionaryEntry keyValue in baseDictionary)
{
if (!(keyValue.Value is TValue))
{
// value is not TKey. perhaps throw an exception
return null;
}
if (!(keyValue.Key is TKey))
{
// value is not TValue. perhaps throw an exception
return null;
}
dictionary.Add((TKey)keyValue.Key, (TValue)keyValue.Value);
}
return dictionary;
}
// object is not a dictionary. perhaps throw an exception
return null;
}
I've done something like this and works for me.
using System.ComponentModel;
var dictionary = new Dictionary<string, string>();
foreach (var propDesc in TypeDescriptor.GetProperties(Obj))
{
if (!string.IsNullOrEmpty(propDesc.GetValue(Obj)))
{
dictionary.Add(propDesc.Name, propDesc.GetValue(Obj));
}
}
Also, another alternative and innovative solution is here.
var dictionary = new System.Web.Routing.RouteValueDictionary(Obj);
I hope this could work :)
// obj = new { a = "string", b = 0, c = true };
static Dictionary<string, object> ToDictionary(object obj)
{
int i = 0;
var props = obj.GetType().GetProperties();
return props.ToDictionary(k => props[i].Name, v => props[i++].GetValue(obj));
}
This code securely works to convert Object to Dictionary (having as premise that the source object comes from a Dictionary):
private static Dictionary<TKey, TValue> ObjectToDictionary<TKey, TValue>(object source)
{
Dictionary<TKey, TValue> result = new Dictionary<TKey, TValue>();
TKey[] keys = { };
TValue[] values = { };
bool outLoopingKeys = false, outLoopingValues = false;
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source))
{
object value = property.GetValue(source);
if (value is Dictionary<TKey, TValue>.KeyCollection)
{
keys = ((Dictionary<TKey, TValue>.KeyCollection)value).ToArray();
outLoopingKeys = true;
}
if (value is Dictionary<TKey, TValue>.ValueCollection)
{
values = ((Dictionary<TKey, TValue>.ValueCollection)value).ToArray();
outLoopingValues = true;
}
if(outLoopingKeys & outLoopingValues)
{
break;
}
}
for (int i = 0; i < keys.Length; i++)
{
result.Add(keys[i], values[i]);
}
return result;
}
This way for object array to Dictionary<string, object> List coverting
object[] a = new object[2];
var x = a.Select(f => (Dictionary<string, object>)f).ToList();
This way for single object to Dictionary<string, object> coverting
object a = new object;
var x = (Dictionary<string, object>)a;
You can create a generic extension method and then use it on the object like:
public static class Extensions
{
public static KeyValuePair<TKey, TValue> ToKeyValuePair<TKey, TValue>(this Object obj)
{
// if obj is null throws exception
Contract.Requires(obj != null);
// gets the type of the obj parameter
var type = obj.GetType();
// checks if obj is of type KeyValuePair
if (type.IsGenericType && type == typeof(KeyValuePair<TKey, TValue>))
{
return new KeyValuePair<TKey, TValue>(
(TKey)type.GetProperty("Key").GetValue(obj, null),
(TValue)type.GetProperty("Value").GetValue(obj, null)
);
}
// if obj type does not match KeyValuePair throw exception
throw new ArgumentException($"obj argument must be of type KeyValuePair<{typeof(TKey).FullName},{typeof(TValue).FullName}>");
}
and usage would be like:
KeyValuePair<string,long> kvp = obj.ToKeyValuePair<string,long>();
I use this simple method:
public Dictionary<string, string> objToDict(XYZ.ObjectCollection objs) {
var dict = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> each in objs){
dict.Add(each.Key, each.Value);
}
return dict;
}
You can use this:
Dictionary<object,object> mydic = ((IEnumerable)obj).Cast<object>().ToList().ToDictionary(px => px.GetType().GetProperty("Key").GetValue(px), pv => pv.GetType().GetProperty("Value").GetValue(pv));
string BaseUrl = "http://www.example.com";
HttpClient client = new HttpClient { BaseAddress = new Uri(BaseUrl) };
PropertyInfo[] properties = object.GetType().GetProperties();
Dictionary<string, string> dictionary = new Dictionary<string, string>();
foreach (PropertyInfo property in properties)
{
dictionary.Add(property.Name, property.GetValue(model, null).ToString());
}
foreach (string key in dictionary.Keys)
{
client.DefaultRequestHeaders.Add(key, dictionary[key]);
}
As I understand it, you're not sure what the keys and values are, but you want to convert them into strings?
Maybe this can work:
public static void MyMethod(object obj)
{
var iDict = obj as IDictionary;
if (iDict != null)
{
var dictStrStr = iDict.Cast<DictionaryEntry>()
.ToDictionary(de => de.Key.ToString(), de => de.Value.ToString());
// use your dictStrStr
}
else
{
// My object is not an IDictionary
}
}
object parsedData = se.Deserialize(reader);
System.Collections.IEnumerable stksEnum = parsedData as System.Collections.IEnumerable;
then will be able to enumerate it!
Simple way:
public IDictionary<T, V> toDictionary<T, V>(Object objAttached)
{
var dicCurrent = new Dictionary<T, V>();
foreach (DictionaryEntry dicData in (objAttached as IDictionary))
{
dicCurrent.Add((T)dicData.Key, (V)dicData.Value);
}
return dicCurrent;
}
I have a json-object in C# (represented as a Newtonsoft.Json.Linq.JObject object) and I need to flatten it to a dictionary. Let me show you an example of what I mean:
{
"name": "test",
"father": {
"name": "test2"
"age": 13,
"dog": {
"color": "brown"
}
}
}
This should yield a dictionary with the following key-value-pairs:
["name"] == "test",
["father.name"] == "test2",
["father.age"] == 13,
["father.dog.color"] == "brown"
How can I do this?
JObject jsonObject=JObject.Parse(theJsonString);
IEnumerable<JToken> jTokens = jsonObject.Descendants().Where(p => !p.HasValues);
Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
{
properties.Add(jToken.Path, jToken.ToString());
return properties;
});
I had the same requirement of flattening a nested json structure to a dictionary object. Found the solution here.
As of .NET Core 3.0 JsonDocument is a way (Json.NET is not needed). I'm sure this will get easier.
using System.Linq;
using System.Text.Json;
(...)
public static Dictionary<string, JsonElement> GetFlat(string json)
{
IEnumerable<(string Path, JsonProperty P)> GetLeaves(string path, JsonProperty p)
=> p.Value.ValueKind != JsonValueKind.Object
? new[] { (Path: path == null ? p.Name : path + "." + p.Name, p) }
: p.Value.EnumerateObject() .SelectMany(child => GetLeaves(path == null ? p.Name : path + "." + p.Name, child));
using (JsonDocument document = JsonDocument.Parse(json)) // Optional JsonDocumentOptions options
return document.RootElement.EnumerateObject()
.SelectMany(p => GetLeaves(null, p))
.ToDictionary(k => k.Path, v => v.P.Value.Clone()); //Clone so that we can use the values outside of using
}
A more expressive version is shown below.
Test
using System.Linq;
using System.Text.Json;
(...)
var json = #"{
""name"": ""test"",
""father"": {
""name"": ""test2"",
""age"": 13,
""dog"": {
""color"": ""brown""
}
}
}";
var d = GetFlat(json);
var options2 = new JsonSerializerOptions { WriteIndented = true };
Console.WriteLine(JsonSerializer.Serialize(d, options2));
Output
{
"name": "test",
"father.name": "test2",
"father.age": 13,
"father.dog.color": "brown"
}
More expressive version
using System.Linq;
using System.Text.Json;
(...)
static Dictionary<string, JsonElement> GetFlat(string json)
{
using (JsonDocument document = JsonDocument.Parse(json))
{
return document.RootElement.EnumerateObject()
.SelectMany(p => GetLeaves(null, p))
.ToDictionary(k => k.Path, v => v.P.Value.Clone()); //Clone so that we can use the values outside of using
}
}
static IEnumerable<(string Path, JsonProperty P)> GetLeaves(string path, JsonProperty p)
{
path = (path == null) ? p.Name : path + "." + p.Name;
if (p.Value.ValueKind != JsonValueKind.Object)
yield return (Path: path, P: p);
else
foreach (JsonProperty child in p.Value.EnumerateObject())
foreach (var leaf in GetLeaves(path, child))
yield return leaf;
}
I actually had the same problem earlier today couldn't find this question on SO at first, and ended up writing my own extension method to return the JValue objects containing the leaf node values of the JSON blob. It's similar to the accepted answer, except for some improvements:
It handles any JSON you give it (arrays, properties, etc) instead of just a JSON object.
Less memory usage
No calls to .Count() on descendants you ultimately don't need
Depending on your use case, those may or may not be relevant, but they are for my case. I wrote about learning to flatten the JSON.NET objects on my blog. Here is the extension method I wrote:
public static class JExtensions
{
public static IEnumerable<JValue> GetLeafValues(this JToken jToken)
{
if (jToken is JValue jValue)
{
yield return jValue;
}
else if (jToken is JArray jArray)
{
foreach (var result in GetLeafValuesFromJArray(jArray))
{
yield return result;
}
}
else if (jToken is JProperty jProperty)
{
foreach (var result in GetLeafValuesFromJProperty(jProperty))
{
yield return result;
}
}
else if (jToken is JObject jObject)
{
foreach (var result in GetLeafValuesFromJObject(jObject))
{
yield return result;
}
}
}
#region Private helpers
static IEnumerable<JValue> GetLeafValuesFromJArray(JArray jArray)
{
for (var i = 0; i < jArray.Count; i++)
{
foreach (var result in GetLeafValues(jArray[i]))
{
yield return result;
}
}
}
static IEnumerable<JValue> GetLeafValuesFromJProperty(JProperty jProperty)
{
foreach (var result in GetLeafValues(jProperty.Value))
{
yield return result;
}
}
static IEnumerable<JValue> GetLeafValuesFromJObject(JObject jObject)
{
foreach (var jToken in jObject.Children())
{
foreach (var result in GetLeafValues(jToken))
{
yield return result;
}
}
}
#endregion
}
Then in my calling code, I just extract the Path and Value properties from the JValue objects returned:
var jToken = JToken.Parse("blah blah json here");
foreach (var jValue in jToken.GetLeafValues())
{
Console.WriteLine("{0} = {1}", jValue.Path, jValue.Value);
}
You can use https://github.com/jsonfx/jsonfx to deserialize json into a dynamic object. Then use the ExpandoObject to get what you want.
public Class1()
{
string json = #"{
""name"": ""test"",
""father"": {
""name"": ""test2"",
""age"": 13,
""dog"": {
""color"": ""brown""
}
}
}";
var reader = new JsonFx.Json.JsonReader();
dynamic output = reader.Read(json);
Dictionary<string, object> dict = new Dictionary<string, object>();
GenerateDictionary((System.Dynamic.ExpandoObject) output, dict, "");
}
private void GenerateDictionary(System.Dynamic.ExpandoObject output, Dictionary<string, object> dict, string parent)
{
foreach (var v in output)
{
string key = parent + v.Key;
object o = v.Value;
if (o.GetType() == typeof(System.Dynamic.ExpandoObject))
{
GenerateDictionary((System.Dynamic.ExpandoObject)o, dict, key + ".");
}
else
{
if (!dict.ContainsKey(key))
{
dict.Add(key, o);
}
}
}
}
You could use the JSONPath $..* to get all members of the JSON structure and filter out the ones with no children to skip the container properties.
e.g.
var schemaObject = JObject.Parse(schema);
var values = schemaObject
.SelectTokens("$..*")
.Where(t => !t.HasValues)
.ToDictionary(t => t.Path, t => t.ToString());
Based on the code that was provided by tymtam, but also supporting arrays:
public static IEnumerable<KeyValuePair<string, string>> Flatten<T>(this T data, string seperator = ":") where T : class
{
var json = JsonSerializer.Serialize(data);
string GetArrayPath(string path, string name, int idx) =>
path == null ? $"{name}{seperator}{idx}" : $"{path}{seperator}{name}{seperator}{idx}";
IEnumerable<(string Path, JsonElement Element)> GetLeaves(string path, string name, JsonElement element) => element.ValueKind switch
{
JsonValueKind.Object => element.EnumerateObject()
.SelectMany(child => GetLeaves(path == null ? name : $"{path}{seperator}{name}", child.Name, child.Value)),
JsonValueKind.Array => element.EnumerateArray()
.SelectMany((child, idx) => child.ValueKind == JsonValueKind.Object
? child.EnumerateObject().SelectMany(child => GetLeaves(GetArrayPath(path, name, idx), child.Name, child.Value))
: new[] { (Path: GetArrayPath(path, name, idx), child) }
),
_ => new[] { (Path: path == null ? name : $"{path}{seperator}{name}", element) },
};
using JsonDocument document = JsonDocument.Parse(json); // Optional JsonDocumentOptions options
return document.RootElement.EnumerateObject()
.SelectMany(p => GetLeaves(null, p.Name, p.Value))
.ToDictionary(k => k.Path, v => v.Element.Clone().ToString()); //Clone so that we can use the values outside of using
}
I really like the ExpandoObject while compiling a server-side dynamic object at runtime, but I am having trouble flattening this thing out during JSON serialization. First, I instantiate the object:
dynamic expando = new ExpandoObject();
var d = expando as IDictionary<string, object>;
expando.Add("SomeProp", SomeValueOrClass);
So far so good. In my MVC controller, I want to then send this down as a JsonResult, so I do this:
return new JsonResult(expando);
This serializes the JSON into the below, to be consumed by the browser:
[{"Key":"SomeProp", "Value": SomeValueOrClass}]
BUT, what I'd really like is to see this:
{SomeProp: SomeValueOrClass}
I know I can achieve this if I use dynamic instead of ExpandoObject -- JsonResult is able to serialize the dynamic properties and values into a single object (with no Key or Value business), but the reason I need to use ExpandoObject is because I don't know all of the properties I want on the object until runtime, and as far as I know, I cannot dynamically add a property to a dynamic without using an ExpandoObject.
I may have to sift through the "Key", "Value" business in my javascript, but I was hoping to figure this out prior to sending it to the client. Thanks for your help!
Using JSON.NET you can call SerializeObject to "flatten" the expando object:
dynamic expando = new ExpandoObject();
expando.name = "John Smith";
expando.age = 30;
var json = JsonConvert.SerializeObject(expando);
Will output:
{"name":"John Smith","age":30}
In the context of an ASP.NET MVC Controller, the result can be returned using the Content-method:
public class JsonController : Controller
{
public ActionResult Data()
{
dynamic expando = new ExpandoObject();
expando.name = "John Smith";
expando.age = 30;
var json = JsonConvert.SerializeObject(expando);
return Content(json, "application/json");
}
}
You could also, make a special JSONConverter that works only for ExpandoObject and then register it in an instance of JavaScriptSerializer. This way you could serialize arrays of expando,combinations of expando objects and ... until you find another kind of object that is not getting serialized correctly("the way u want"), then you make another Converter, or add another type to this one. Hope this helps.
using System.Web.Script.Serialization;
public class ExpandoJSONConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var result = new Dictionary<string, object>();
var dictionary = obj as IDictionary<string, object>;
foreach (var item in dictionary)
result.Add(item.Key, item.Value);
return result;
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new ReadOnlyCollection<Type>(new Type[] { typeof(System.Dynamic.ExpandoObject) });
}
}
}
Using converter
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJSONConverter()});
var json = serializer.Serialize(obj);
Here's what I did to achieve the behavior you're describing:
dynamic expando = new ExpandoObject();
expando.Blah = 42;
expando.Foo = "test";
...
var d = expando as IDictionary<string, object>;
d.Add("SomeProp", SomeValueOrClass);
// After you've added the properties you would like.
d = d.ToDictionary(x => x.Key, x => x.Value);
return new JsonResult(d);
The cost is that you're making a copy of the data before serializing it.
I solved this by writing an extension method that converts the ExpandoObject into a JSON string:
public static string Flatten(this ExpandoObject expando)
{
StringBuilder sb = new StringBuilder();
List<string> contents = new List<string>();
var d = expando as IDictionary<string, object>;
sb.Append("{");
foreach (KeyValuePair<string, object> kvp in d) {
contents.Add(String.Format("{0}: {1}", kvp.Key,
JsonConvert.SerializeObject(kvp.Value)));
}
sb.Append(String.Join(",", contents.ToArray()));
sb.Append("}");
return sb.ToString();
}
This uses the excellent Newtonsoft library.
JsonResult then looks like this:
return JsonResult(expando.Flatten());
And this is returned to the browser:
"{SomeProp: SomeValueOrClass}"
And I can use it in javascript by doing this (referenced here):
var obj = JSON.parse(myJsonString);
I hope this helps!
I was able to solve this same problem using JsonFx.
dynamic person = new System.Dynamic.ExpandoObject();
person.FirstName = "John";
person.LastName = "Doe";
person.Address = "1234 Home St";
person.City = "Home Town";
person.State = "CA";
person.Zip = "12345";
var writer = new JsonFx.Json.JsonWriter();
return writer.Write(person);
output:
{ "FirstName": "John", "LastName": "Doe", "Address": "1234 Home St",
"City": "Home Town", "State": "CA", "Zip": "12345" }
I took the flattening process one step further and checked for list objects, which removes the key value nonsense. :)
public string Flatten(ExpandoObject expando)
{
StringBuilder sb = new StringBuilder();
List<string> contents = new List<string>();
var d = expando as IDictionary<string, object>;
sb.Append("{ ");
foreach (KeyValuePair<string, object> kvp in d)
{
if (kvp.Value is ExpandoObject)
{
ExpandoObject expandoValue = (ExpandoObject)kvp.Value;
StringBuilder expandoBuilder = new StringBuilder();
expandoBuilder.Append(String.Format("\"{0}\":[", kvp.Key));
String flat = Flatten(expandoValue);
expandoBuilder.Append(flat);
string expandoResult = expandoBuilder.ToString();
// expandoResult = expandoResult.Remove(expandoResult.Length - 1);
expandoResult += "]";
contents.Add(expandoResult);
}
else if (kvp.Value is List<Object>)
{
List<Object> valueList = (List<Object>)kvp.Value;
StringBuilder listBuilder = new StringBuilder();
listBuilder.Append(String.Format("\"{0}\":[", kvp.Key));
foreach (Object item in valueList)
{
if (item is ExpandoObject)
{
String flat = Flatten(item as ExpandoObject);
listBuilder.Append(flat + ",");
}
}
string listResult = listBuilder.ToString();
listResult = listResult.Remove(listResult.Length - 1);
listResult += "]";
contents.Add(listResult);
}
else
{
contents.Add(String.Format("\"{0}\": {1}", kvp.Key,
JsonSerializer.Serialize(kvp.Value)));
}
//contents.Add("type: " + valueType);
}
sb.Append(String.Join(",", contents.ToArray()));
sb.Append("}");
return sb.ToString();
}
JsonResult uses JavaScriptSerializer which actually deserializes (the concrete) Dictionary<string, object> as you want.
There's an overload of the Dictionary<string, object> constructor which takes IDictionary<string, object>.
ExpandoObject implements IDictionary<string, object> (I think you can see where I am going here...)
Single level ExpandoObject
dynamic expando = new ExpandoObject();
expando.hello = "hi";
expando.goodbye = "cya";
var dictionary = new Dictionary<string, object>(expando);
return this.Json(dictionary); // or new JsonResult { Data = dictionary };
One line of code, using all built-in types :)
Nested ExpandoObjects
Of course if you are nesting ExpandoObjects then you will need to recursively convert them all into Dictionary<string, object>s:
public static Dictionary<string, object> RecursivelyDictionary(
IDictionary<string, object> dictionary)
{
var concrete = new Dictionary<string, object>();
foreach (var element in dictionary)
{
var cast = element.Value as IDictionary<string, object>;
var value = cast == null ? element.Value : RecursivelyDictionary(cast);
concrete.Add(element.Key, value);
}
return concrete;
}
your final code becoming
dynamic expando = new ExpandoObject();
expando.hello = "hi";
expando.goodbye = "cya";
expando.world = new ExpandoObject();
expando.world.hello = "hello world";
var dictionary = RecursivelyDictionary(expando);
return this.Json(dictionary);
This may not be useful to you, but I had a similar requirement, but used a SerializableDynamicObject
I changed the name of the dictionary to "Fields" and then this serializes with Json.Net to produce json which looks like:
{"Fields":{"Property1":"Value1", "Property2":"Value2" etc.
where Property1 and Property2 are Dynamically added properties - i.e. Dictionary Keys
It would be perfect if I could get rid of the extra "Fields" property which encapsulates the rest, but I've worked around that limitation.
Answer moved from this question on request
This is a late answer, but I had the same problem, and this question helped me solve them.
As a summary, I thought I should post my results, in hopes that it speeds up the implementation for others.
First the ExpandoJsonResult, which you can return an instance of in your action. Or you can override the Json method in your controller and return it there.
public class ExpandoJsonResult : JsonResult
{
public override void ExecuteResult(ControllerContext context)
{
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType) ? ContentType : "application/json";
response.ContentEncoding = ContentEncoding ?? response.ContentEncoding;
if (Data != null)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoConverter() });
response.Write(serializer.Serialize(Data));
}
}
}
Then the converter (which supports both serialization and de-serialization. See below for an example of how to de-serialize).
public class ExpandoConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{ return DictionaryToExpando(dictionary); }
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{ return ((ExpandoObject)obj).ToDictionary(x => x.Key, x => x.Value); }
public override IEnumerable<Type> SupportedTypes
{ get { return new ReadOnlyCollection<Type>(new Type[] { typeof(System.Dynamic.ExpandoObject) }); } }
private ExpandoObject DictionaryToExpando(IDictionary<string, object> source)
{
var expandoObject = new ExpandoObject();
var expandoDictionary = (IDictionary<string, object>)expandoObject;
foreach (var kvp in source)
{
if (kvp.Value is IDictionary<string, object>) expandoDictionary.Add(kvp.Key, DictionaryToExpando((IDictionary<string, object>)kvp.Value));
else if (kvp.Value is ICollection)
{
var valueList = new List<object>();
foreach (var value in (ICollection)kvp.Value)
{
if (value is IDictionary<string, object>) valueList.Add(DictionaryToExpando((IDictionary<string, object>)value));
else valueList.Add(value);
}
expandoDictionary.Add(kvp.Key, valueList);
}
else expandoDictionary.Add(kvp.Key, kvp.Value);
}
return expandoObject;
}
}
You can see in the ExpandoJsonResult class how to use it for serialization. To de-serialize, create the serializer and register the converter in the same way, but use
dynamic _data = serializer.Deserialize<ExpandoObject>("Your JSON string");
A big thank you, to all participants here that helped me.
Using returning dynamic ExpandoObject from WebApi in ASP.Net 4, the default JSON formatter seems to flatten ExpandoObjects into simple JSON object.
It seems like the serializer is casting the Expando to a Dictionary and then serializing it (thus the Key/Value business). Have you tried Deserializing as a Dictionary and then casting that back to an Expando?
I just had the same problem and figured out something pretty weird.
If I do:
dynamic x = new ExpandoObject();
x.Prop1 = "xxx";
x.Prop2 = "yyy";
return Json
(
new
{
x.Prop1,
x.Prop2
}
);
It works, but only if my method use HttpPost attribute. If I use HttpGet i get error.
So my answer works only on HttpPost. In my case it was an Ajax Call so i could change HttpGet by HttpPost.