2

I have no issue deserializing JSON into known types or into Dictionary objects, but what about cases where I don't know what the input is going to be? Specifically I'm referring to receiving a JSON string that represents a flat or nested set of key-value pairs:

{ 
  foo: 'bar',
  baz: 42
}

or

{
  foo: 
  {
    bar: 42,
    baz: ['foo', 'bar', 'baz']
  }
}

But what about cases where the input isn't a key-value-pair, but rather an array, or, an array of objects with other nested objects (including arrays)?

[
  { foo: 'bar', baz: [ 1, 2, 3 ] },
  { foo: 'baz', bar: [ 4, 5, 6 ] }
]

My goal is to have a single class that I could deserialize any of the above into, and then iterate each of its members. The input could be of any structure, so I can't assume the data will come in matching any type I've already defined.

I haven't been able to find a way to do this. Anyone have any guidance?

Edit:

Seems easy enough to JToken.Parse the JSON string; a helpful next step would be to iterate its members and handle JArray and JObject separately.

joelc
  • 2,687
  • 5
  • 40
  • 60
  • Have you considered deserialising into a dynamic? http://stackoverflow.com/questions/4535840/deserialize-json-object-into-dynamic-object-using-json-net – Dr Rob Lang Oct 10 '16 at 16:09
  • Yes, I can't seem to get it to work when the input is an array with no key. – joelc Oct 10 '16 at 16:17
  • What do you mean by an array with no key? – Abion47 Oct 10 '16 at 16:31
  • 1
    What you are describing is the [LINQ-to-JSON API](http://www.newtonsoft.com/json/help/html/linqtojson.htm) (JTokens). Use `JToken token = JToken.Parse(json)`. Or if you prefer plain old .Net objects, see [How do I use JSON.NET to deserialize into nested/recursive Dictionary and List?](http://stackoverflow.com/a/19140420/10263) – Brian Rogers Oct 10 '16 at 17:19
  • Thanks Brian, can you show an example of how to detect if each node is an array or key-value pair and iterate (so I can award you the answer) – joelc Oct 10 '16 at 18:15
  • You will need to write custom converter extending JsonConverter and on ReadJson function. – sam Oct 10 '16 at 20:02
  • 1
    @joelc I've added an answer which shows how to iterate over the members of a JToken. – Brian Rogers Oct 10 '16 at 21:12

1 Answers1

3

What you are describing already exists in Json.Net-- it is the LINQ-to-JSON API (JTokens). Below is an example of using it to parse arbitrary JSON and iterate through the members. Note that you need a recursive method to do it, since the JSON can be nested to any depth.

using Newtonsoft.Json.Linq;
public class Program
{
    public static void Main(string[] args)
    {
        string json1 = @"
        {
          ""foo"": { ""bar"": 42, ""baz"": [ ""a"", ""b"", ""c"" ] }
        }";
        DeserializeAndDump(json1, "example 1");

        string json2 = @"
        [
          { ""foo"": ""bar"", ""baz"": [ 1, 2, 3 ] },
          { ""foo"": ""baz"", ""bar"": [ 4, 5, 6 ] }
        ]";
        DeserializeAndDump(json2, "example 2");
    }

    public static void DeserializeAndDump(string json, string label)
    {
        Console.WriteLine("---- " + label + " ----");
        JToken token = JToken.Parse(json);
        DumpJToken(token);
        Console.WriteLine();
    }

    public static void DumpJToken(JToken token, string indent = "")
    {
        if (token.Type == JTokenType.Object)
        {
            Console.WriteLine(indent + "begin object");
            foreach (JProperty prop in token.Children<JProperty>())
            {
                Console.WriteLine(indent + "  " + prop.Name + ":");
                DumpJToken(prop.Value, indent + "    ");
            }
            Console.WriteLine(indent + "end object");
        }
        else if (token.Type == JTokenType.Array)
        {
            Console.WriteLine(indent + "begin array");
            foreach (JToken child in token.Children())
            {
                DumpJToken(child, indent + "  ");
            }
            Console.WriteLine(indent + "end array");
        }
        else
        {
            Console.WriteLine(indent + token.ToString() + " (" + token.Type.ToString() + ")");
        }
    }
}

Here is the output of the above:

---- example 1 ----
begin object
  foo:
    begin object
      bar:
        42 (Integer)
      baz:
        begin array
          a (String)
          b (String)
          c (String)
        end array
    end object
end object

---- example 2 ----
begin array
  begin object
    foo:
      bar (String)
    baz:
      begin array
        1 (Integer)
        2 (Integer)
        3 (Integer)
      end array
  end object
  begin object
    foo:
      baz (String)
    bar:
      begin array
        4 (Integer)
        5 (Integer)
        6 (Integer)
      end array
  end object
end array

Fiddle: https://dotnetfiddle.net/dfk0sj

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300