I'm gathering data from a web API which returns a complex JSON string. This contains values of many different types, a few arrays, and some fairly deep nesting.
Currently I'm converting the string to a dynamic object like so:
dynamic dynamicObject = JsonConvert.DeserializeObject(jsonString);
Then I'm retrieving values from this object as needed and typecasting them, however this method gives a null reference exception when I attempt to retrieve an array:
int myValue = (int)dynamicObject.foo.value; // Works!
int[] myArrayOfInts = (int[])dynamicObject.bar.values; // Null
I'm not sure how to proceed as I'm fairly new to C# and ASP.net. Most of the solutions I have come across so far require the creation of small, strictly-typed classes which mirror the entire JSON structure. In my case it would be preferable to simply use a dynamic object, partly for simplicity and partly because only certain values are actually being used.
When I try to run your code, I get a RuntimeBinderException: Cannot convert type 'Newtonsoft.Json.Linq.JArray' to 'int[]'. You can do this conversion with ToObject.
dynamic dynamicObject = JsonConvert.DeserializeObject("{\"values\": [1, 3, 4]}");
int[] myArrayOfInts = dynamicObject.values.ToObject<int[]>();
The cast to int works because a casting conversion is defined for many types including int, but not int[].
You could deserialize the array using the ToObject<type> extension method (where type is the desired data type):
var jsonString = #"{ ""name"":""test"", ""array"": [""10"",""20"",""30"",""40""] }";
dynamic dynamicObject = JsonConvert.DeserializeObject(jsonString);
Console.WriteLine(((string)dynamicObject.name));
var items = dynamicObject.array.ToObject<int[]>();
foreach (var item in items)
{
Console.WriteLine(item);
}
The output is:
test
10
20
30
40
Another possibility would be to cast the object to JObject and then fetch the array as a property - the retrieved object is automatically of type JToken, which allows iteration:
var array = ((JObject)dynamicObject)["array"];
foreach (var item in array)
{
Console.WriteLine(item.ToString());
}
Related
In C#, I am reading a property Config of type IDictionary<string, object>. I am unable to access the information inside the objectelements. 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 configItemsElementis
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.
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.
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);
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:
declare my own type for the anonymous type
cast configItemsElement to IEnumerable
iterate over the dictionary inside of configItemsElement
get the 'inner item' of each dictionary item using reflection
cast the 'inner item' (of anonymous type) to dynamic
and use reflection to access the 2 properties of the 'inner item' (value and sortOrder) to add them to a new list
This 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!
I am having trouble understanding when to use JContainer, JObject, and JToken. I understand from the "standards" that JObject is composed of JProperties and that JToken is the base abstract class for all of the JToken types, but I don't understand JContainer.
I am using C# and I just bought LinqPad Pro 5.
I have a JSON data source in a file, so I'm deserializing that file's contents successfully using this statement:
string json;
using (StreamReader reader = new StreamReader(#"myjsonfile.json"))
{
json = reader.ReadToEnd();
}
At that point, I take the JSON string object and deserialize it to a JObject (and this might be my mistake--perhaps I need to make jsonWork a JToken or JContainer?):
JObject jsonWork = (JObject)JsonConvert.DeserializeObject(json);
In my JSON data (the string represented by JSON), I have three objects--the top-level object look similar to this:
{
"Object1" : { ... },
"Object2" : { ... },
"Object3" : { ... }
}
Each object is composed of all sorts of tokens (arrays, strings, other objects, etc.), so it is dynamic JSON. (I used ellipses as placeholders rather than muddying up this question wit lots of JSON data.)
I want to process "Object1", "Object2", and "Object3" separately using LINQ, however. So, ideally, I would like something like this:
// these lines DO NOT work
var jsonObject1 = jsonWork.Children()["Object1"]
var jsonObject2 = jsonWork.Children()["Object2"]
var jsonObject3 = jsonWork.Children()["Object3"]
But the above lines fail.
I used var above because I have no idea what object type I should be using: JContainer, JObject, or JToken! Just so you know what I want to do, once the above jsonObject# variables are properly assigned, I would like to use LINQ to query the JSON they contain. Here is a very simple example:
var query = from p in jsonObject1
where p.Name == "Name1"
select p
Of course, my LINQ ultimately will filter for JSON arrays, objects, strings, etc., in the jsonObject variable. I think once I get going, I can use LinqPad to help me filter the JSON using LINQ.
I discovered that if I use:
// this line WORKS
var jsonObject1 = ((JObject)jsonWork).["Object1"];
Then I get an JObject type in jsonObject1. Is this the correct approach?
It is unclear to me when/why one would use JContainer when it seems that JToken and JObject objects work with LINQ quite well. What is the purpose of JContainer?
You don't really need to worry about JContainer in most cases. It is there to help organize and structure LINQ-to-JSON into well-factored code.
The JToken hierarchy looks like this:
JToken - abstract base class
JContainer - abstract base class of JTokens that can contain other JTokens
JArray - represents a JSON array (contains an ordered list of JTokens)
JObject - represents a JSON object (contains a collection of JProperties)
JProperty - represents a JSON property (a name/JToken pair inside a JObject)
JValue - represents a primitive JSON value (string, number, boolean, null)
So you see, a JObject is a JContainer, which is a JToken.
Here's the basic rule of thumb:
If you know you have an object (denoted by curly braces { and } in JSON), use JObject
If you know you have an array or list (denoted by square brackets [ and ]), use JArray
If you know you have a primitive value, use JValue
If you don't know what kind of token you have, or want to be able to handle any of the above in a general way, use JToken. You can then check its Type property to determine what kind of token it is and cast it appropriately.
JContainer is a base class for JSON elements that have child items. JObject, JArray, JProperty and JConstructor all inherit from it.
For example, the following code:
(JObject)JsonConvert.DeserializeObject("[1, 2, 3]")
Would throw an InvalidCastException, but if you cast it to a JContainer, it would be fine.
Regarding your original question, if you know you have a JSON object at the top level, you can just use:
var jsonWork = JObject.Parse(json);
var jsonObject1 = jsonWork["Object1"];
Most examples have simple json and I've googled "C# Newtonsoft parse JSON" more than once.
Here's a bit of a json file I was just asked to parse for a csv. The company name value is nested within many arrays / objects so it is semi-complicated in that regard.
{
"page": {
"page": 1,
"pageSize": 250
},
"dataRows": [
{
"columnValues": {
"companyName": [
{
"name": "My Awesome Company",
}
]
}
}
]
}
var jsonFilePath = #"C:\data.json";
var jsonStr = File.ReadAllText(jsonFilePath);
// JObject implementation for getting dataRows JArray - in this case I find it simpler and more readable to use a dynamic cast (below)
//JObject jsonObj = JsonConvert.DeserializeObject<JObject>(jsonStr);
//var dataRows = (JArray)jsonObj["dataRows"];
var dataRows = ((dynamic)JsonConvert.DeserializeObject(jsonStr)).dataRows;
var csvLines = new List<string>();
for (var i = 0; i < dataRows.Count; i++)
{
var name = dataRows[i]["columnValues"]["companyName"][0]["name"].ToString();
// dynamic casting implemntation to get name - in this case, using JObject indexing (above) seems easier
//var name2 = ((dynamic)((dynamic)((dynamic)dataRows[i]).columnValues).companyName[0]).name.ToString();
csvLines.Add(name);
}
File.WriteAllLines($#"C:\data_{DateTime.Now.Ticks}.csv", csvLines);
How can i unserialize a list of different objects uwing JSON.net?
string myJson = "[{action: 'a1', target: 4},{action: 'a2', targets: [1,2,3], {action:'a3', text:'targets altered'}}]";
This example presents a list of 3 different objects.
If I unserialize as a basic Object, I cannot access any members (as the base Object class doesn't have them).
I've looked over http://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm but I don't understand if it can help me.
with that method you posted, your json should contain extra info to indicate what type it is,which means you need to modify how the source of json to serialize,and make sure the you use the same class,namespace as the source serialized.
Or,use Object or dynamic as type of target, and to convert it in later use may be easier.
Use the dynamic type.
You can access elements as if they were properties. If a property doesn't exist, the dynamic type will return null instead of throwing an exception
dynamic actions = JsonConvert.DeserializeObject(myJson);
foreach (var action in actions)
{
var name = action.action.ToString();
var target = action.target.ToString();
var text = action.text.ToString();
if (target == null)
{
dynamic targets = action.targets;
}
}
I'm trying to create a dynamic object from a JSON string in C#. But i can't get it done.
Normally i would get a JSON string through a web service call but in this case I simply created a simple class which I turn into a JSON string. Then I try to turn it back into a dynamic object with the exact same structure as if it was an instance of the Entity class. But that's where I'm having trouble.
This is the class that i turn into a JSON string:
public class Entity
{
public String Name = "Wut";
public String[] Scores = {"aaa", "bbb", "ccc"};
}
Then in somewhere in my code i do this:
var ent = new Entity();
// The Serialize returns this:
// "{\"Name\":\"Wut\",\"Scores\":[\"aaa\",\"bbb\",\"ccc\"]}"
var json = new JavaScriptSerializer().Serialize(ent);
dynamic dynamicObject1 = new JavaScriptSerializer().DeserializeObject(json);
dynamic dynamicObject2 = Json.Decode(json);
When I debug this code then i see that the first dynamicObject1 returns a Dictionary. Not really what I'm after.
The second dynamicObject2 looks more like the Entity object. It has a property called Name with a value. It also has a dynamic array called Scores, but for some reason that list turns out to be empty...
Screenshot of empty Scores property in dynamic object:
So I'm not having any luck so far trying to cast a JSON string to a dynamic object. Anyone any idea how I could get this done?
Using Json.Net
dynamic dynamicObject1 = JsonConvert.DeserializeObject(json);
Console.WriteLine(dynamicObject1.Name);
Using Json.Net but deserializing into a ExpandableOBject, a type that is native to C#:
dynamic obj= JsonConvert.DeserializeObject<ExpandoObject>(yourjson);
If the target type is not specified then it will be convert to JObject type instead. Json object has json types attached to every node that can cause problems when converting the object into other dynamic type like mongo bson.
Is it possible to convert a a IEnumerable<KeyValuePair<string,string>> of KeyValuePair to an anonymous type?
Dictionary<string, string> dict= new Dictionary<string, string>();
dict.add("first", "hello");
dict.add("second", "world");
var anonType = new{dict.Keys[0] = dict[0], dict.Keys[1] = dict[1]};
Console.WriteLine(anonType.first);
Console.WriteLine(anonType.second);
********************output*****************
hello
world
The reason i would like to do this is because I am retrieving an object from a webservice that represents an object that does not exist in the wsdl. The returned object only contains a KeyValuePair collection that contains the custom fields and their values. These custom fields can be named anything, so i cant really map an xml deserialization method to the final object i will be using (whose properties must be bound to a grid).
*Just because I used Dictionary<string,string> does not mean it is absolutely a dictionary, i just used it for illustration. Really its an IEnumerable<KeyValuePair<string,string>>
Ive been trying to thing of a way to do this, but am drawing a blank. This is c# .NET 4.0.
You could use the ExpandoObject, it is based on a dictionary.
I think there are a lot of ways to achieve this, but actually converting it in the same Dictionary seems a bit odd to do.
One way to accomplish this, by not actually converting everyting is the following:
public class MyDictionary<T,K> : Dictionary<string,string> // T and K is your own type
{
public override bool TryGetValue(T key, out K value)
{
string theValue = null;
// magic conversion of T to a string here
base.TryGetValue(theConvertedOfjectOfTypeT, out theValue);
// Do some magic conversion here to make it a K, instead of a string here
return theConvertedObjectOfTypeK;
}
}
ExpandoObject is the best option, which I believe is a wrapper around some XML. You could also use an XElement:
var result = new XElement("root");
result.Add(new XElement("first", "hello"));
result.Add(new XElement("second", "world"));
Console.WriteLine(result.Element("first").Value);
Console.WriteLine(result.Element("second").Value);
foreach (var element in result.Elements())
Console.WriteLine(element.Name + ": " + element.Value);
I haven't used ExpandoObject, so I'd try that first because I understand it does exactly what you want and is also something new and interesting to learn.