How to Deserialize the following Json string in C#?

Viewed 60

I'm trying to deserialize the following JSON:

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "frameworks": [
      {
        "name": "Microsoft.NETCore.App",
        "version": "6.0.0"
      },
      {
        "name": "Microsoft.WindowsDesktop.App",
        "version": "6.0.0"
      }
    ],
    "configProperties": {
      "MaxTargets" : "1024"
   }
  }  
}

My model class looks like:

public class OptionalInclude
{
    [JsonProperty("configProperties")]
    public string ConfigProperties { get; set; }

    [JsonProperty("tfm")]
    public string Tfm { get; set; }

    [JsonProperty("frameworks")]
    public IList<Dictionary<string, string>> Frameworks { get; set; }
}

I'm trying to deserialize in this way, but I need the MaxTargets value from the JSON:

var optionalIncludesConfig = File.ReadAllText(configPath);
result = (JObject)JsonConvert.DeserializeObject(optionalIncludesConfig);

var optionIncludes = new List<OptionalInclude>();
optionIncludes = result.ToObject<List<OptionalInclude>>();
4 Answers

configProperties in your JSON is really a object, and you are forgetting about your top level object runtimeOptions. You should change your model to be:

public class TopLevel //this should be something more descriptive - only used as an example
{
    [JsonProperty("runtimeOptions")]
    public RuntimeOptions runtimeOptions { get; set; }
}

public class RuntimeOptions
{
    [JsonProperty("configProperties")]
    public ConfigProperties ConfigProperties { get; set; }

    [JsonProperty("tfm")]
    public string Tfm { get; set; }

    [JsonProperty("frameworks")]
    public IList<Dictionary<string, string>> Frameworks { get; set; }
}

public class ConfigProperties
{
    [JsonProperty("maxTargets")]
    public string MaxTargets { get; set; }
}

Then to deserialize, you should be able to deserialize into an TopLevel object and retrieve configProperties to get your MaxTargets:

var optionalIncludesConfig = File.ReadAllText(configPath);
var result = JsonConvert.DeserializeObject<TopLevel>(optionalIncludesConfig);

string maxTargets = result.runtimeOptions.ConfigProperties.MaxTargets;

It should look something like this as seen in this HttpPost example (where the post body is the JSON found in your question):

enter image description here

Firstly, the ConfigProperties property on OptionalInclude is not a string type in the JSON, it is an object, so you should hard-type it like this:

public class ConfigProperties
{
    [JsonProperty("MaxTargets")]
    public string MaxTargets { get; set; }
}

Secondly, I think the frameworks in your JSON would make more sense to have a Framework class like this:

public class Framework
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("version")]
    public string Version { get; set; }
}

and use an List<Framework> instead. Lastly, you either need a class like this:

public class SomeClassName
{
    [JsonProperty("runtimeOptions")]
    public OptionalInclude RuntimeOptions { get; set; }
}

and then deserialize into that, or you need to parse the JSON into a JObject, navigate it into the first layer of the object (the "runtimeOptions" object) and then parse that value into OptionalInclude (I'd just hard-type it, personally).

Your resulting OptionalInclude class should end up like this:

public class OptionalInclude
{
    [JsonProperty("configProperties")]
    public ConfigProperties ConfigProperties { get; set; }

    [JsonProperty("tfm")]
    public string Tfm { get; set; }

    [JsonProperty("frameworks")]
    public List<Framework> Frameworks { get; set; }
}

Then you should be able to deserialize it like this:

var jsonString = File.ReadAllText(configPath);
var result = JsonConvert.DeserializeObject<SomeClassName>(jsonString);

Note specifically that you can DeserializeObject and use a specific object type instead of casting to a JObject.

Here is a .NET Fiddle of the full code.

Approach 1

You can access attributes of a JObject with the [ ] operator, for example assuming json is a JObject containing the json you provided:

var maxTargets = json["runtimeOptions"]["configProperties"]["MaxTargets"].ToString();

I find this approach fast, but it's easy to mess up with null tokens and such. Also, this isn't a very polished soluton.

Approach 2

I personally like to have classes for nested jsons, so for example:

public class ConfigProperties {
    [JsonProperty("MaxTargets")]
    public string MaxTargets { get; set; }
}

and then add it to your outer class:

public class OptionalInclude {
    // other stuff
    public ConfigProperties Properties { get; set; }
}

then you will be able do deserialize it. However, I see you are using a List of OptionalInclude. Assuming that you are cycling through such list:

foreach (var item in optionalIncludes) {
   var maxTargets = item.Properties.MaxTargets;
   // your logic
}

configProperties is not an enumerable (list)

You can make:

public class Configuration
{
    [JsonProperty("runtimeOptions")]
    public RuntimeOptions RuntimeOptions { get; set; }
}

public class RuntimeOptions
{
    [JsonProperty("tfm")]
    public string Tfm { get; set; }

    [JsonProperty("frameworks")]
    public List<Framework> Frameworks { get; set; }

    [JsonProperty("configProperties")]
    public ConfigProperties ConfigProperties { get; set; }
}

public class Framework
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("version")]
    public string Version { get; set; }
}

public class ConfigProperties
{
    [JsonProperty("MaxTargets")]
    public string MaxTargets { get; set; }
}

Then you code:

var configuration = File.ReadAllText(configPath);
var result = JsonSerializer.Deserialize<Configuration>(configuration);

string optionInclude = result.RuntimeOptions.ConfigProperties.MaxTargets;
Related