If you want to deserialize JSON without having to create a bunch of classes, you can either deserialize to a dictionary or deserialize to a dynamic object with Newtonsoft.Json. Here’s an example. Let’s say you want to deserialize the following JSON:
{
"name":"Bob",
"favorites":{
"number":7,
"food":"cheese"
}
}
Code language: JSON / JSON with Comments (json)
To deserialize this to a dynamic object with Newtonsoft, use JsonConvert.DeserializeObject<dynamic>:
using Newtonsoft.Json;
var person = JsonConvert.DeserializeObject<dynamic>(json);
Console.WriteLine($"{person.name}'s favorite number is {person.favorites.number}")
Console.WriteLine($"{person.name}'s favorite food is {person.favorites.food}");
Code language: C# (cs)
This outputs the following:
Bob's favorite number is 7
Bob's favorite food is cheese
Code language: plaintext (plaintext)
Table of Contents
Deserialize to ExpandoObject in older versions of Newtonsoft
Before Newtonsoft v4.0.1 (released in 2014), specifying <dynamic> didn’t work. When you tried to access a dynamic property on JObject, you’d get an exception like this:
JObject does not contain a definition for property
If you’re using an older version of Newtonsoft (before v4.0.1):
- Use JsonConvert.DeserializeObject<ExpandoObject>
- Pass in ExpandoObjectConverter.
Here’s an example:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
dynamic config = JsonConvert.DeserializeObject<ExpandoObject>(json, new ExpandoObjectConverter());
Code language: C# (cs)
Example – Deserialize JSON array to dynamic object
Here’s another example of deserializing JSON to a dynamic object with Newtonsoft. Let’s say you’re deserializing JSON that contains an array of objects:
{
"version":1.0,
"endpoints": [
{
"name": "prod",
"enabled": true
},
{
"name": "dev",
"enabled": true
},
{
"name": "qa",
"enabled": false
}
]
}
Code language: JSON / JSON with Comments (json)
You can deserialize this to dynamic and loop over the array with a regular foreach:
using Newtonsoft.Json;
var apiConfig = JsonConvert.DeserializeObject<dynamic>(json);
Console.WriteLine($"Using api verison: {apiConfig.version}");
foreach(var endpoint in apiConfig.endpoints)
{
Console.WriteLine($"{endpoint.name} enabled? {endpoint.enabled}");
}
Code language: C# (cs)
This outputs the following:
Using api verison: 1
prod enabled? True
dev enabled? True
qa enabled? False
Code language: plaintext (plaintext)
Cast to IEnumerable<dynamic> to use Linq methods
If you want to use a Linq method on the dynamic object’s array, cast it to IEnumerable<dynamic> first, like this:
Console.WriteLine("Enabled endpoints:");
foreach (var enabledEndpoint in ((IEnumerable<dynamic>)apiConfig.endpoints).Where(t => t.enabled))
{
Console.WriteLine($"{enabledEndpoint.name}");
}
Code language: C# (cs)
This gets rid of compiler error CS1977 – Cannot use lambda expression as argument to dynamically dispatched… .
System.Text.Json vs Newtonsoft.Json
The built-in System.Text.Json doesn’t work well for deserializing to dynamic objects:
dynamic config = System.Text.Json.JsonSerializer.Deserialize<dynamic>(json);
Code language: C# (cs)
When you try to deserialize to dynamic or ExpandoObject, System.Text.Json deserializes it to JsonElement. This is fine if you want to work with JSON DOM (read: using JsonDocument and using JsonNode). However this defeats the purpose of deserializing to dynamic. For true dynamic support, stick with Newtonsoft.
Amazing ! Like magic !!! Thanks!
Thanks Mak – this is a real life problem for developers… 🙂
How can we handle the situation where the properties are dynamic could be added or deleted based on JSON response
i.e. Name & Enabled
In future if we get Name, Enabled and Owner
… is there a way to dynamically cater this?
You can check if the dynamic object (ExpandoObject) has a property by casting it to an IDictionary<string, object>. Its Keys are the properties.
Using the example code from this article, let’s say I want to check if one of the endpoint objects has a property called “owner”.
I would check that by doing this:
if ((enabledEndpoint as IDictionary<string, object>).ContainsKey("owner"))
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
dynamic config = JsonConvert.DeserializeObject<ExpandoObject>(data, new ExpandoObjectConverter());
}
I get following error.
InvalidCastException: Unable to cast object of type ‘System.Collections.Generic.List`1[System.Object]’ to type ‘System.Dynamic.ExpandoObject’.
Looks like you are deserializing a JSON array. Use IEnumerable<ExpandoObject> as the type parameter instead of ExpandoObject, like this:
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
dynamic config = JsonConvert.DeserializeObject<IEnumerable<ExpandoObject>>(data, new ExpandoObjectConverter());
}
“In older versions of Newtonsoft, when you tried to access a dynamic property on JObject, you’d get an exception like this [..]”
Could you tell me in which version they fixed it? 😉
Version 4.0.1
Example:-
dynamic config = JsonConvert.DeserializeObject(data, new ExpandoObjectConverter());
Can we try to update some values here in dynamic config object and then convert it back to the json. Would that be possible?
Yes, you can do that. Here’s an example.
//1. Get JSON as dynamic object
string json = "{\"Enabled\":true}";
dynamic config = JsonConvert.DeserializeObject<ExpandoObject>(json, new ExpandoObjectConverter());
//2. Change a value, and serialize back to JSON
config.Enabled = false;
var changedJson = JsonConvert.SerializeObject(config);
Console.WriteLine(changedJson);
This outputs the changed JSON: { "Enabled": false }