I am trying to deserialize my JSON object and pass it as a model to my view. Since I don't know what properties the model will have, I have read that I should use an ExpandoObject.
Here is what I have tried:
public ActionResult Index()
{
var myObj = new object();
List<Dictionary<string, object>> container = new List<Dictionary<string, object>>()
{
new Dictionary<string, object> { { "Text", "Hello world" } }
};
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
myObj = json_serializer.DeserializeObject(json_serializer.Serialize(container));
return View(myObj.ToExpando());
}
And, in the same namespace I have defined this class:
public static class Helpers
{
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(anonymousObject);
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in anonymousDictionary)
expando.Add(item);
return (ExpandoObject)expando;
}
}
And, in my view I have this loop:
#foreach (var item in Model)
{
#item.Text
}
When I run, I get this error:
'System.Collections.Generic.KeyValuePair' does not
contain a definition for 'Text'
Upon debugging, the Model doesn't seem to have any public properties. When I look deep within the private members, I see the data that I want.
Why aren't these public properties such that I can access them?
Edit: Here you can see the expando object model that is getting passed to my view:
Note: The SyncRoot property seems to contain my object.
Edit: This is the deserialized object:
Notice that #item is defined as a System.Collections.Generic.KeyValuePair based on your error.
These means that you have two properties: Key and Value.
Here are two possible solution:
#foreach (var item in Model.Values)
{
#item.Id
}
or
#foreach (var item in Model)
{
#item.Value.Id
}
The solution for me was to do something like this (using the ExpandoObjectConverter):
var myObj = new object();
List<Dictionary<string, object>> container = new List<Dictionary<string, object>>()
{
new Dictionary<string, object> { { "Text", "Hello world" } }
};
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
var converter = new ExpandoObjectConverter();
var obj = JsonConvert.DeserializeObject<IEnumerable<ExpandoObject>>(json_serializer.Serialize(container), converter);
return View(obj);
However, this doesn't account for deeply nested JSON objects. I can probably create some sort of recursive method.
It's sad that the framework doesn't support such an obvious requirement; unless I am missing something.
Related
I have a C# dictionary in which I have a corresponding NAME against the ID.
Dictionary<string, List<object>> dict = new Dictionary<string, List<object>>
{
{ "ID", new List<object> { "Id1", "Id2" } },
{ "NAME", new List<object> { "True", "False" } }
};
foreach (var id in dict["ID"])
{
Console.WriteLine(id);
//how to get corresponding "Name". For "Id1" = "True" and for "Id2" = "False"
}
In above code I loop through ID, but how to get corresponding NAME?
I think a better design would be to create a class with the two properties and then iterate. If you find yourself having to sync different data structures for simple data representations then I'd suggest rethinking the design.
public class MyClass
{
public string Id { get; set; }
public bool Name { get; set; }
}
And then hold a List<MyClass> which when you iterate:
foreach (var item in list)
{
// Now access item.Id, item.Name
}
The use of dictionaries is good when you have some sort of natural key for your data and you want to access access an item by that key. As the items are accessed via a hash function accessing by key is done in O(1) whereas searching in a list is O(n). However in your case you are iterating all items in any case so no need for dictionary and arranging the data in a class is a better design.
A bit about the differences and some references:
what is the difference between list<> and dictionary<> in c#
List vs ArrayList vs Dictionary vs Hashtable vs Stack vs Queue?
If you do have control over dictionary data it's best to either use Gilad's answer and store everything in List<MyClass> or to use Dictionary<string, bool> :
Dictionary<string, bool> dict = new Dictionary<string, bool>()
{
{ "Id1", true }, { "Id2", false },
};
But if you do not have control over format of this data and get it as a dictionary from somewhere (for example web service) you could utilize .Zip method to convert this dictionary into one list of either anonymous objects/custom class or Tuples, where Item1 is Id and Item2 is value:
// anonymous object
var data = dict["ID"].Zip(dict["NAME"], (x, y) => new
{
ID = x,
NAME = y
}).ToList();
// tuple
// List<Tuple<object, object>> data = dict["ID"].Zip(dict["NAME"], Tuple.Create).ToList();
foreach (var obj in data)
{
Console.WriteLine(obj.ID + " " obj.NAME);
}
The other answers are probably what you should do to better structure your code. However, if you need to stick to your original use case, you could do something like this:
//Depending on what you're dealing with: Dictionary<string, List<string>>
Dictionary<string, List<object>> dict = new Dictionary<string, List<object>>{
{"ID", new List<object>{"Id1", "Id2"}},
{"NAME", new List<object>{"True", "False"}}
};
foreach(var v in dict.Keys){
Console.WriteLine($"{v} = {string.Join(",", dict[v])}");
}
//Output:
//ID = Id1,Id2
//NAME = True,False
Even if you have the just the mapping of ID and Name you can have very simple variable
Dictionary<string,string> lookup = new Dictionary<string,string>();
lookup.Add("ID1","True")
and if Name is Boolean type then replace string to bool in the
Dictionary<string,bool> lookup = new Dictionary<string,bool>();
I have the following class:
public class Test
{
public Dictionary<string, string> dict = new Dictionary<string, string>();
public static void main(String args[]){
var serializer = new JavaScriptSerializer();
Test tt = new Test();
tt.dict.Add("hello","divya");
tt.dict.Add("bye", "divya");
String s = serializer.Serialize(tt.dict); // s is {"hello":"divya","bye":"divya"}
Test t = (Test)serializer.Deserialize(s,typeof(Test));
Console.WriteLine(t.dict["hello"]); // gives error since dict is empty
}
So the question is how do I deserialize a json string like {"hello":"divya","bye":"divya"} into a strongly typed object containing a dictionary.
To deserialize that into a Dictionary, the JSON would have to look a little different. It would have to define the Test class (loosely):
{
dict: {
"hello": "divya",
"bye": "divya"
}
}
See, the dict definition exists in the JSON. However, what you have there could be deserialized directly into the Dictionary like this:
tt.dict = (Dictionary<string, string>)serializer.Deserialize(s,
typeof(Dictionary<string, string>));
I am very new to .NET, used to working in PHP. I need to iterate via foreach through a dictionary of objects. My setup is an MVC4 app.
The Model looks like this:
public class TestModels
{
Dictionary<int, dynamic> sp = new Dictionary<int, dynamic>
{
{1, new {name="abc", age="1"}},
{2, new {name="def", age="2"}}
}
}
Controller:
public class TestController : Controller
{
Models.TestModels obj = new Models.TestModels();
}
How do I loop through the obj object and retrieve the values of the dictionary and print them in the view?
One way is to loop through the keys of the dictionary, which I recommend:
foreach(int key in sp.Keys)
dynamic value = sp[key];
Another way, is to loop through the dictionary as a sequence of pairs:
foreach(KeyValuePair<int, dynamic> pair in sp)
{
int key = pair.Key;
dynamic value = pair.Value;
}
I recommend the first approach, because you can have more control over the order of items retrieved if you decorate the Keys property with proper LINQ statements, e.g., sp.Keys.OrderBy(x => x) helps you retrieve the items in ascending order of the key. Note that Dictionary uses a hash table data structure internally, therefore if you use the second method the order of items is not easily predictable.
Update (01 Dec 2016): replaced vars with actual types to make the answer more clear.
It depends on what you are after in the Dictionary
Models.TestModels obj = new Models.TestModels();
foreach (var keyValuPair in obj.sp)
{
// KeyValuePair<int, dynamic>
}
foreach (var key in obj.sp.Keys)
{
// Int
}
foreach (var value in obj.sp.Values)
{
// dynamic
}
You can do it like this.
Models.TestModels obj = new Models.TestModels();
foreach (var item in obj.sp)
{
Console.Write(item.Key);
Console.Write(item.Value.name);
Console.Write(item.Value.age);
}
The problem you most likely have right now is that the collection is private. If you add public to the beginning of this line
Dictionary<int, dynamic> sp = new Dictionary<int, dynamic>
You should be able to access it from the function inside your controller.
Edit: Adding functional example of the full TestModels implementation.
Your TestModels class should look something like this.
public class TestModels
{
public Dictionary<int, dynamic> sp = new Dictionary<int, dynamic>();
public TestModels()
{
sp.Add(0, new {name="Test One", age=5});
sp.Add(1, new {name="Test Two", age=7});
}
}
You probably want to read up on the dynamic keyword as well.
public class TestModels
{
public Dictionary<int, dynamic> sp = new Dictionary<int, dynamic>();
public TestModels()
{
sp.Add(0, new {name="Test One", age=5});
sp.Add(1, new {name="Test Two", age=7});
}
}
I am working with Automapper and need to achieve the following mapping but not sure how it can be done.
I want to map a Dictionary object to a dynamic object, so that the key is the property on the object and the value of the dictionary is the value of property in dynamic object.
Can this be achieve with automapper and if so, how?
You can simply get Dictionary from ExpandoObject and fill it with original dictionary values
void Main()
{
AutoMapper.Mapper.CreateMap<Dictionary<string, object>, dynamic>()
.ConstructUsing(CreateDynamicFromDictionary);
var dictionary = new Dictionary<string, object>();
dictionary.Add("Name", "Ilya");
dynamic dyn = Mapper.Map<dynamic>(dictionary);
Console.WriteLine (dyn.Name);//prints Ilya
}
public dynamic CreateDynamicFromDictionary(IDictionary<string, object> dictionary)
{
dynamic dyn = new ExpandoObject();
var expandoDic = (IDictionary<string, object>)dyn;
dictionary.ToList()
.ForEach(keyValue => expandoDic.Add(keyValue.Key, keyValue.Value));
return dyn;
}
Here's en example, but if you drop a comment or elaborate your post it could be more descriptive. Given this class:
class Foo
{
public Foo(int bar, string baz)
{
Bar = bar;
Baz = baz;
}
public int Bar { get; set; }
public string Baz { get; set; }
}
You can create a dictionary of its public instance properties and values this way:
var valuesByProperty = foo.GetType().
GetProperties(BindingFlags.Public | BindingFlags.Instance).
ToDictionary(p => p, p => p.GetValue(foo));
If you want to include more or different results, specify different BindingFlags in the GetProperties method. If this doesn't answer your question, please leave a comment.
Alternatively, assuming you're working with a dynamic object and anonymous types, the approach is similar. The following example, clearly, doesn't require the class Foo.
dynamic foo = new {Bar = 42, Baz = "baz"};
Type fooType = foo.GetType();
var valuesByProperty = fooType.
GetProperties(BindingFlags.Public | BindingFlags.Instance).
ToDictionary(p => p, p => p.GetValue(foo));
I am trying to serialize objects with the library ServiceStack.Text . This works
using System.Dynamic;
using ServiceStack.Text;
var x = new {Value= 10, Product = "Apples"};
Console.WriteLine(JsonSerializer.SerializeToString(x));
I get, as I expect
{"Value":10,"Product":"Apples"}
However
dynamic x = new ExpandoObject();
x.Value = 100;
x.Product = "Apples";
Console.WriteLine(JsonSerializer.SerializeToString(x));
I get to my surprise
[{"Key":"Value","Value":100},{"Key":"Product","Value":"Apples"}]
Why! What's going on?
Secondly, how can I get what I want?
ExpandoObject implements IConnection<KeyValuePair> and IEnumerable<KeyValuePair>:
public sealed class ExpandoObject :
IDynamicMetaObjectProvider,
IDictionary<string, object>,
ICollection<KeyValuePair<string, object>>,
IEnumerable<KeyValuePair<string, object>>,
IEnumerable, INotifyPropertyChanged
My guess is that internally, the ServiceStack serializer is treating the ExpandoObject as an IEnumerable<KeyValuePair>, and so it serializes to a JSON array of key/value pairs.
This differs from your first (working) code snippet because .NET actually builds a real (anonymous) class for your data, basically it makes:
public class SomeNameTheCompilerMakesUp {
internal int Value { get; set; }
internal string Product { get; set; }
}
for you automatically, so when it is sent to the serializer, it is working with a real class with real properties, whereas the ExpandoObject is really backed by an object[] internally.
On a side-note, Microsoft's System.Web.Helpers.Json behaves the same way. This test passes:
[TestMethod]
public void ExpandoObjectSerializesToJsonArray()
{
dynamic anonType = new { Value = 10, Product = "Apples" };
dynamic expando = new ExpandoObject();
expando.Value = 10;
expando.Product = "Apples";
var anonResult = System.Web.Helpers.Json.Encode(anonType);
var expandoResult = System.Web.Helpers.Json.Encode(expando);
Assert.AreEqual("{\"Value\":10,\"Product\":\"Apples\"}", anonResult);
Assert.AreEqual("[{\"Key\":\"Value\",\"Value\":10},{\"Key\":\"Product\",\"Value\":\"Apples\"}]", expandoResult);
}
One final edit:
You can make this work the way you want by turning your ExpandoObject into a Dictionary<string, object>. The caveat to this code is that it duplicates the data into a dictionary, so you have 2 copies in memory (or slightly less than, since technically the strings might be interned).
[TestMethod]
public void TestMethod1()
{
dynamic expando = new ExpandoObject();
expando.Value = 10;
expando.Product = "Apples";
// copy expando properties to dictionary
var dictionary = ((ExpandoObject)expando).ToDictionary(x => x.Key, x => x.Value);
var expandoResult = System.Web.Helpers.Json.Encode(expando);
var dictionaryResult = System.Web.Helpers.Json.Encode(dictionary);
Assert.AreEqual("[{\"Key\":\"Value\",\"Value\":10},{\"Key\":\"Product\",\"Value\":\"Apples\"}]", expandoResult);
Assert.AreEqual("{\"Value\":10,\"Product\":\"Apples\"}", dictionaryResult);
}
Although, for anyone that comes across this later, and is actually using System.Web.Helpers.Json, the better thing to do is just wrap your ExpandoObject in a DynamicJsonObject like this:
[TestMethod]
public void TestMethod1()
{
dynamic expando = new ExpandoObject();
expando.Value = 10;
expando.Product = "Apples";
var dictionaryResult = System.Web.Helpers.Json.Encode(new DynamicJsonObject(expando));
Assert.AreEqual("{\"Value\":10,\"Product\":\"Apples\"}", dictionaryResult);
}
Ans once I worked through that, I found a similar question here: How to flatten an ExpandoObject returned via JsonResult in asp.net mvc?