If you want to deserialize JSON without having to create a bunch of classes, use Newtonsoft.Json like this:
dynamic config = JsonConvert.DeserializeObject<ExpandoObject>(json, new ExpandoObjectConverter());
Code language: C# (cs)
Now you can use this object like any other object.
Table of Contents
Example
Add the Newtonsoft.Json reference

Code
string json = "{\"endpoints\":[{\"name\":\"prod\", \"enabled\":true },{\"name\":\"dev\", \"enabled\":true},{\"name\":\"qa\", \"enabled\":false}]}"; //see JSON section below for pretty printed JSON
dynamic config = JsonConvert.DeserializeObject<ExpandoObject>(json, new ExpandoObjectConverter());
Console.WriteLine($"Deserialized JSON into {config.GetType()}");
foreach (var enabledEndpoint in ((IEnumerable<dynamic>)config.endpoints).Where(t => t.enabled))
{
Console.WriteLine($"{enabledEndpoint.name} is enabled");
}
Code language: C# (cs)
Output:

JSON
{
"endpoints": [
{
"name": "prod",
"enabled": true
},
{
"name": "dev",
"enabled": true
},
{
"name": "qa",
"enabled": false
}
]
}
Code language: JSON / JSON with Comments (json)
Dynamic vs ExpandoObject
If you specify <dynamic> instead of <ExpandoObject> it’ll create a JObject.
In older versions of Newtonsoft, 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
This is no longer the case. You can use JObject just like ExpandoObject in new versions of Newtonsoft. However, I wanted to show a code example that will definitely work in whatever version of Newtonsoft you’re using.
Why cast to (IEnumerable<dynamic>)?
If you try to use config.endpoints.Where() like this:
foreach (var enabledEndpoint in config.endpoints.Where(t => t.enabled))
Code language: C# (cs)
You’ll get the following compile error:
Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type.
This is why we have to cast it to (IEnumerable<dynamic>).
System.Text.Json vs Newtonsoft.Json
I tried to deserialize into a dynamic object using the built-in System.Text.Json, but it doesn’t work well. I tried it using .NET Core 3.1.
Example:
dynamic config = System.Text.Json.JsonSerializer.Deserialize<ExpandoObject>(json);
Code language: C# (cs)
System.Text.Json deserializes this into an ExpandoObject with JsonElement properties. In my example, config.endpoints is a JsonElement. In order to loop over this, I’d have to use config.endpoints.EnumerateArray(). This returns JsonElement objects.
It’s JsonElements all the way down. In other words, this is not a nice clean way to deserialize JSON into a dynamic object.
Perhaps System.Text.Json will be improved later on, but as of right now, Newtonsoft.Json is better.
Go with Newtonsoft.Json.