2

I want to take some Json and parse it in to a collection of key/value pairs, but some of the values will be dictionaries themselves. I tried the usual Newtonsoft deserialization. It's close, but not quite right. The end result must be a dictionary, not a strongly typed class.

This is some example Json:

{
  "JobNumber": 1010,
  "Asset": null,
  "JobNotes": [
    {
      "NoteText": "It's not working.",
      "NoteType": "Complaint"
    },
    {
      "NoteText": "Needs to be fixed",
      "NoteType": "Job"
    }
  ]
}

This is the code I used to deserialize:

        var json = File.ReadAllText(@"c:\temp\job.json");
        var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

The result is almost correct, but the value of the item with a key of "JobNotes" is just json string. I want the parser to recurse in and deserialise the inner Json to a further dictionary of strings and objects. Is there a way I can do this with the Newtonsoft library? Or, is there another library that will do the trick? Can I hook in to the parsing method to override the functionality at that point in time?

Christian Findlay
  • 6,770
  • 5
  • 51
  • 103
  • Do you *need* the `Dictionary` or would you be okay with a strongly typed result? – vzwick Oct 31 '17 at 23:28
  • If you have strongly typed classes with an array of `JobNotes` it will automatically parse it and populate the collection. – TheVillageIdiot Oct 31 '17 at 23:29
  • Have you considered deserializing the JSON string into a dynamic? Take look at this question: [Deserialize JSON into C# dynamic object?](https://stackoverflow.com/questions/3142495/deserialize-json-into-c-sharp-dynamic-object) – Ellesedil Oct 31 '17 at 23:29
  • I can't use a strongly typed result @vzwick because I don't know the target type. – Christian Findlay Oct 31 '17 at 23:30
  • I'd be happy to use dynamic, but from looking at that article, I can't really see how it applies here @Ellesedil. – Christian Findlay Oct 31 '17 at 23:34
  • I'm not clear how you want your dictionary for `JobNotes` to look. Its an array so its not clear if you changed it to a dictionary what the key would be for each item in that array... – Chris Nov 01 '17 at 00:09
  • This might help you https://stackoverflow.com/questions/3142495/deserialize-json-into-c-sharp-dynamic-object – Vijay Pendse Nov 01 '17 at 00:10

4 Answers4

3

This is a modified version of @DanielKeogh's code. It works well.

class Program
{
    static void Main(string[] args)
    {
        var json = File.ReadAllText(@"c:\temp\job3.json");
        var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
        RecurseDeserialize(result);
    }

    private static void RecurseDeserialize(Dictionary<string, object> result)
    {
        //Iterate throgh key/value pairs
        foreach (var keyValuePair in result.ToArray())
        {
            //Check to see if Newtonsoft thinks this is a JArray
            var jarray = keyValuePair.Value as JArray;

            if (jarray != null)
            {
                //We have a JArray

                //Convert JArray back to json and deserialize to a list of dictionaries
                var dictionaries = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(jarray.ToString()); 

                //Set the result as the dictionary
                result[keyValuePair.Key] = dictionaries;

                //Iterate throught the dictionaries
                foreach (var dictionary in dictionaries)
                {
                    //Recurse
                    RecurseDeserialize(dictionary);
                }
            }
        }
    }
}

This modified Json shows how deep it goes:

{
  "JobNumber": 1010,
  "Asset": null,
  "JobNotes": [
    {
      "NoteText": "It's not working.",
      "NoteType": "Complaint"
    },
    {
      "NoteText": "Needs to be fixed",
      "NoteType": "Job",
      "JobNoteNotes": [
        {
          "Something": 1,
          "Something2": "Test"
        }
      ]
    }
  ]
}

The result ends three dictionaries deep so that I can get at the "Something" value by key.

Christian Findlay
  • 6,770
  • 5
  • 51
  • 103
1

This can be done with a little recursion. I'll leave defining IsJson up to you as an academic exercise. :)

Dictionary<string, object> RecursiveDeserialize(string json)
{
     var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
     foreach (var pair in result.ToArray())
     {
         if(IsJson(pair.Value))
         {
             result[pair.Key] = RecursiveDeserialize(pair.Value);
         }
     }
     return result;
}
Daniel Keogh
  • 233
  • 1
  • 7
0

Using this object for json string

public class JobNote
{
    public string NoteText { get; set; }
    public string NoteType { get; set; }
}

public class ListJob
{
    public int JobNumber { get; set; }
    public object Asset { get; set; }
    public List<JobNote> JobNotes { get; set; }
}

Then you can deserialize it

Le Tung Anh
  • 811
  • 6
  • 7
0

Did a modification on @christian-findlay 's answer to also convert JObject types to Dictionary<string, object> when iterating:

private static void RecurseDeserialize(Dictionary<string, object> result)
    {
        foreach (var keyValuePair in result.ToArray())
        {
            var jarray = keyValuePair.Value as JArray;

            if (jarray != null)
            {
                var dictionaries = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(jarray.ToString());
                result[keyValuePair.Key] = dictionaries;

                foreach (var dictionary in dictionaries)
                {
                    RecurseDeserialize(dictionary);
                }
            }
            else 
            {
                var jobject = keyValuePair.Value as JObject;
                if (jobject != null)
                {
                    var dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(jobject.ToString());
                    result[keyValuePair.Key] = dictionary;
                    RecurseDeserialize(dictionary);
                }
            }
        }
    }