Home > Software engineering >  How to deserialize json with nest array of dynamic keys in C#
How to deserialize json with nest array of dynamic keys in C#

Time:09-23

I have the following json structure from an api return:

{
  "firstName": "test first",
  "lastName": "test last",
  "moduleReports": [
    {
      "completion": "Nothing shared",
      "thoughts": "example",
      "componentTitle": "CareerCorner"
    },
    {
      "thoughts": "example",
      "componentTitle": "CareerLIbrary"
    },
    {
      "completionDate": "0001-01-01T00:00:00",
      "componentTitle": "Discoverer"
    },
    {
      "completionDate": "0001-01-01T00:00:00",
      "componentTitle": "Explorer"
    },
    {
      "workValues": [
        "independence",
        "relationships"
      ],
      "componentTitle": "Navigator"
    },
    {
      "personalityTypes": [
        "Creator",
        "Doer"
      ],
      "componentTitle": "Pathfinder"
    },
    {
      "reflection": "example",
      "completionDate": "0001-01-01T00:00:00",
      "componentTitle": "QuickPic"
    },
    {
      "careerGroup": "Ideas",
      "componentTitle": "Scout"
    },
    {
      "improveAtSkills": [
        "listen",
        "speak"
      ],
      "goodAtSkills": [
        "decision",
        "solve"
      ],
      "componentTitle": "Trekker"
    }
  ]
}

I tried to create the classes myself and came up with

public class ModuleReport
  {
    public string completion { get; set; }
    public string thoughts { get; set; }
    public string componentTitle { get; set; }
    public DateTime? completionDate { get; set; }
    public List<string> workValues { get; set; }
    public List<string> personalityTypes { get; set; }
    public string reflection { get; set; }
    public string careerGroup { get; set; }
    public List<string> improveAtSkills { get; set; }
    public List<string> goodAtSkills { get; set; }
  }

  public class ModuleResult
  {
    public string firstName { get; set; }
    public string lastName { get; set; }
    public List<ModuleReport> moduleReports { get; set; }
  }

but when I call

var mResults = JsonConvert.DeserializeObject<List<ModuleResult>>( jsonString );

in the controller I get the following error

Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[...Reports.User.ModuleResults]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.

I have tried following the advice found in the answers here: How to deserialize JSON with dynamic and static key names in C# and hereSerialize/Deserialize dynamic property name using JSON.NET but I keep getting the same error.

I started to build the following

public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
    {
      var responseObject = JObject.Load( reader );

      ModuleResult response = new ModuleResult
      {
        firstName = (string)responseObject[ "firstName" ],
        lastName = (string)responseObject[ "lastName" ],
      };

      var varData = new Dictionary<string, object>();

      foreach ( var property in responseObject.Properties() )
      {
        if ( property.Name == "firstName" || property.Name == "lastName" )
        {
          continue;
        }

        varData.Add( property.Name, property.Value );
      }

      response.VarData = varData;
      return response;
    }

but when I put it into place I realized that it would just add the entire "moduleResults" node as key and all it's contents as the value. So back to square one.

I know the issue is accessing the "moduleReports" json array and properly deserializing those key/value pairs, but I'm having a hell of a time figuring out how to dig in to that nested array and then how to deal with the changing key/value pairs.

CodePudding user response:

Why do you want to deserialize it as List<ModuleResult>, when the JSON structure you've provided indicates that it is a single ModuleResult object?

Just change

var mResults = JsonConvert.DeserializeObject<List<ModuleResult>>( jsonString ); 

To

var mResults = JsonConvert.DeserializeObject<ModuleResult>( jsonString ); 

and it will be deserialized correctly.

CodePudding user response:

try this

var jD = JsonConvert.DeserializeObject<Root>(json);

output=JsonConvert.SerializeObject(jD); 

output

{"firstName":"test first","lastName":"test last","moduleReports":[{"completion":"Nothing shared","thoughts":"example","componentTitle":"CareerCorner"},{"thoughts":"example","componentTitle":"CareerLIbrary"},{"componentTitle":"Discoverer","completionDate":"0001-01-01T00:00:00-03:30"},{"componentTitle":"Explorer","completionDate":"0001-01-01T00:00:00-03:30"},{"componentTitle":"Navigator","workValues":["independence","relationships"]},{"componentTitle":"Pathfinder","personalityTypes":["Creator","Doer"]},{"componentTitle":"QuickPic","completionDate":"0001-01-01T00:00:00-03:30","reflection":"example"},{"componentTitle":"Scout","careerGroup":"Ideas"},{"componentTitle":"Trekker","improveAtSkills":["listen","speak"],"goodAtSkills":["decision","solve"]}]}

classes

public partial class Root
    {
        [JsonProperty("firstName")]
        public string FirstName { get; set; }

        [JsonProperty("lastName")]
        public string LastName { get; set; }

        [JsonProperty("moduleReports")]
        public ModuleReport[] ModuleReports { get; set; }
    }

    public partial class ModuleReport
    {
        [JsonProperty("completion", NullValueHandling = NullValueHandling.Ignore)]
        public string Completion { get; set; }

        [JsonProperty("thoughts", NullValueHandling = NullValueHandling.Ignore)]
        public string Thoughts { get; set; }

        [JsonProperty("componentTitle")]
        public string ComponentTitle { get; set; }

        [JsonProperty("completionDate", NullValueHandling = NullValueHandling.Ignore)]
        public DateTimeOffset? CompletionDate { get; set; }

        [JsonProperty("workValues", NullValueHandling = NullValueHandling.Ignore)]
        public string[] WorkValues { get; set; }

        [JsonProperty("personalityTypes", NullValueHandling = NullValueHandling.Ignore)]
        public string[] PersonalityTypes { get; set; }

        [JsonProperty("reflection", NullValueHandling = NullValueHandling.Ignore)]
        public string Reflection { get; set; }

        [JsonProperty("careerGroup", NullValueHandling = NullValueHandling.Ignore)]
        public string CareerGroup { get; set; }

        [JsonProperty("improveAtSkills", NullValueHandling = NullValueHandling.Ignore)]
        public string[] ImproveAtSkills { get; set; }

        [JsonProperty("goodAtSkills", NullValueHandling = NullValueHandling.Ignore)]
        public string[] GoodAtSkills { get; set; }
    }
  • Related