Reputation: 373
In C#, I am reading a property Config
of type IDictionary<string, object>
. I am unable to access the information inside the object
elements. It should be a JSON string, however it is wrapped as object.
In VisualStudio's Immediate window, typing Config
outputs:
Count = 1
[0]: {[items, Count = 3]}
Drilling down into items:
var configItemsElement = Config["items"]
Typing configItemsElement
in Immediate window outputs:
Count = 3
[0]: {[1, {{ value = SeoChoiceInherit, sortOrder = 1 }}]}
[1]: {[2, {{ value = SeoChoiceYes, sortOrder = 2 }}]}
[2]: {[3, {{ value = SeoChoiceNo, sortOrder = 3 }}]}
and typing configItemsElement.ToString()
returns this type information:
System.Collections.Generic.Dictionary`2[System.String,<>f__AnonymousType7`2[System.String,System.Int32]]
This is as far as I get. I cannot access the elements of configItemsElement
. For instance
configItemsElement[1]
results in error CS0021: Cannot apply indexing with [] to an expression of type 'object'
In VisualStudio's "Autos" window, the information for configItemsElement
is
configItemsElement
Value: Count = 3
Type: object {System.Collections.Generic.Dictionary<string, <>f__AnonymousType7<string, int>>}
What does this type
information mean? It hints at a dictionary, yet applying a key with configItemsElement[1]
results in the error above. How can I cast this object to a type I can use? Any cast I try results in errors.
EDIT:
I have no access to the code providing the .Config
property, but I can see in SQL explorer the data being read into the property. SQL Explorer shows this data:
{"items":[{"id":1,"value":"SeoChoiceInherit"},{"id":2,"value":"SeoChoiceYes"},{"id":3,"value":"SeoChoiceNo"}]}
Making configItemsElement
dynamic, and accessing .value
, as suggested in symap's answer, like:
dynamic configItemsElement = Config["Items"];
var myString =configItemsElement.value;
results in error:
''object' does not contain a definition for 'value''
which I understand, since configItemsElement
should contain a dictionary . But, as mentioned above, accessing dictionary elements doesn`t work either.
Upvotes: 0
Views: 961
Reputation: 373
I answer my own question since the valuable input I received pointed me to the right direction, yet did not give me a working solution. I am rather new to Stackoverflow, so if there is something I should learn about this, please let me know.
Also, although I found a working solution, I do not understand why the suggestions by @symaps and @shf301 do not work in this case. This is why I am grateful for further input.
The way I finally got it to work is to:
configItemsElement
to IEnumerableconfigItemsElement
value
and sortOrder
) to add them to a new listThis is my working code:
...
public class InnerItemType
{
public int sortOrder { get; set; }
public string value { get; set; }
}
....
List<InnerItemType> listOptions = new List<InnerItemType>();
var configItemsElement = Config["items"] as IEnumerable;
foreach (var item in configItemsElement)
{
dynamic innerItem = item.GetType().GetProperties()[1].GetValue(item);
listOptions.Add(new InnerItemType() {
value = innerItem.GetType().GetProperties()[0].GetValue(innerItem),
sortOrder = innerItem.GetType().GetProperties()[1].GetValue(innerItem)
});
}
...
Thanks to @symaps for pointing out that dynamics need to be used. Thanks to @shf301 for explaining that the anonymous type is the cause of the difficulty. This pointed me to the right direction.
However, curiously, the solution with just dynamics (without reflection) does not work as suggested, in my case, and I don't understand why this is.
item.GetType().GetProperties()[1]
reveales the name of the innerItem:
{<>f__AnonymousType7`2[System.String,System.Int32] Value}
...
Name: "Value"
but if I try to get to innerItem through a dynamic type using the property name like this:
foreach (dynamic item in configItemsElement)
{
dynamic innerItem = item.Value;
I get an error "'System.ValueType' does not contain a definition for 'Value'"
Equally, if I try accessing innerItem.value
and innerItem.sortOrder
directly without reflection like so:
foreach (var item in configItemsElement)
{
dynamic innerItem = item.GetType().GetProperties()[1].GetValue(item);
listOptions.Add(new InnerItemType() {
value = innerItem.value,
sortOrder = innerItem.sortOrder
});
gives the same error.
Do I misunderstand the works of dynamic
? So, although I found a working solution, I would like to fully understand the underlying problem and I am grateful for further explanation. Thank you!
Upvotes: 1
Reputation: 62
You can use a dynamic to access the members without type checking.
using System;
using System.Collections;
using System.Collections.Generic;
namespace ObjectDictionary
{
class Program
{
static void Main(string[] args)
{
IDictionary <string, object> dict= new Dictionary<String, object>();
dict.Add("abc", new {value = "blah", sortOrder = 1});
dynamic a = dict["abc"];
Console.WriteLine(a.value);
Console.WriteLine(a.sortOrder);
}
}
}
so your
var configItemsElement = Config["items"];
would become
dynamic configItemsElement = Config["Items"];
var myString =configItemsElement.value;
Looking at your question the data does not appear to be stored as Json. If you need it as json then you would need to reconstruct the json. You could probably use a json Serializer
string json = JsonConvert.SerializeObject(configItemsElement, Formatting.Indented);
Console.WriteLine(json);
Upvotes: 1
Reputation: 31394
The type System.Collections.Generic.Dictionary<string, <>f__AnonymousType7<string, int>>
means that you have a dictionary where the keys are strings and the values are anonymous types. Since anonymous types have no name (from C#'s perspective anyway, as you can see they get a generated name in the compiled code) there's no usable type for you to cast the values to.
Your issue occurred at the point where the configuration was parsed from JSON. Anonymous types should generally only be used inside if a single methods. If they leak out as object
references you get the problems that you are having. If possible the code that generated those configuration elements should be fixed to use a named type.
However if that's no possible your only fallback is to cast the values dynamic
.
Upvotes: 2