0

I'm having trouble figuring out how to parse a JSON object with anonymous objects. Here is my JSON:

{
    "success": true,
    "affectedRows": 2,
    "data": [
        {
            "success": true,
            "affectedRows": 1,
            "data": [
                {
                    "ID": 376,
                    "SomeOtherID": 0,
                    "StringValue": "Fan"
                }
            ]
        },
        {
            "success": true,
            "affectedRows": 1,
            "data": []
        },
        {
            "success": true,
            "data": {
                "ID": 401,
                "DateTime": "2014-10-03 18:52:48"
            }
        }
    ]
}

I have a class that contains classes that work as models for the JSON response I'm expecting.

public class JSONObjects
{
    public class Success
    {
        public bool success {get;set}
        public int affectedRows {get;set;}
    }

    public class Response
    {
        public int ID {get;set;}
        public int SomeOtherID {get;set;}
        public string StringValue {get;set;}
    }

    public class FirstResponse : Success
    {
        public Response[] data {get;set;}
    }

    public class SecondResponse : Success
    {
        public object[] data {get;set;}
    }

    public class TimeData
    {
        public int ID {get;set;}
        public string DateTime {get;set;}
    }

    public class FullTimeData
    {
        public bool success {get;set;}
        public TimeData data {get;set;}
    }

    public class FullResponse : Success
    {
        // ??? anonymous object[] data ???
    }
}

usage:

FullResponse data = JsonConvert.DeserializeObject<JSONObjects.FullResponse>(jsonString, jsonSerializerSettings);
Debug.WriteLine(data.data[0].anonymousObject0.data[0].StringValue);

How can I place the anonymous objects in the FullResponse class as an array of objects and then later access them?

Jared Price
  • 5,217
  • 7
  • 44
  • 74
  • I have trouble figuring out why such JSON? Something you came up with or just something you have to use? – Mikko Viitala Oct 23 '14 at 19:41
  • More or less it's what I have to use. I COULD change some things on the back end, but that would not be ideal. – Jared Price Oct 23 '14 at 19:44
  • Hi it would be great if you can share the design thats behind this problem. I mean the functionality you are trying to achieve from UI perspective ? – Thanigainathan Oct 23 '14 at 21:26
  • Is the JSON format always the exact structure above, or does it change? If it changes, what parts are variable, and what parts are fixed? Is the structure fully recursive? Are you open to the idea of using `JObjects` instead of fixed classes? – Brian Rogers Oct 25 '14 at 19:53

1 Answers1

1

Mmm, that's a toughie. First off, the only way you can use anonymous types outside the local context in which they were created is to use dynamic. It is possible to deserialize JSON into wholly dynamic types, but NewtonSoft.Json won't do it out of the box; more info can be found here. However, I would avoid using dynamic at almost any cost, as it bypasses the most powerful tool a C# developer has for producing correct code; the compiler syntax checks. Fat-finger an identifier, or use square brackets instead of parenthesis on a method call, and as long as it could be valid C# syntax, the compiler will no longer care whether it actually is valid, leaving you to find out at runtime.

Staying in static land, the "data" field is going to be your biggest problem in deserializing this, because it's an array type in all but one instance, where it's a struct type. Without a discriminator of some kind, or a one-to-one relationship between a field name and a field type, I can't think of a way to deserialize this structure into a strongly-typed graph as-is.

Let's say that "data" is always an array; the only change in the JSON would be a set of square brackets around the actual data struct for the last object:

{
    "success": true,
    "affectedRows": 2,
    "data": [
        {
            "success": true,
            "affectedRows": 1,
            "data": [
                {
                    "ID": 376,
                    "SomeOtherID": 0,
                    "StringValue": "Fan"
                }
            ]
        },
        {
            "success": true,
            "affectedRows": 1,
            "data": []
        },
        {
            "success": true,
            "data": [
                {
                    "ID": 401,
                    "DateTime": "2014-10-03 18:52:48"
                }
            ]
        }
    ]
}

In that case, what I would do is define a single, self-nesting type that has a property for every data field you could find at any level of this graph:

public class RawResponse
{
    public bool? success {get;set}
    public int? affectedRows {get;set;}

    public int? ID {get;set;}
    public int? SomeOtherID {get;set;}
    public string StringValue {get;set;}
    public string DateTime {get;set;}

    public RawResponse[] data {get;set;}
}

This will allow you to deserialize the JSON into a statically-typed object graph. Then, you can add a method that produces a particular derived implementation of a Response, based on what fields are set:

public class RawResponse : Response
{
    public bool? success {get;set}
    public int? affectedRows {get;set;}

    public int? ID {get;set;}
    public int? SomeOtherID {get;set;}
    public string StringValue {get;set;}
    public string DateTime {get;set;}

    public RawResponse[] data {get;set;}

    public Response ToResponse()
    {
        if(ID.HasValue && SomeOtherID.HasValue && StringValue.)
            return new OtherIdResponse{
                                           ID = ID, 
                                           SomeOtherID = SomeOtherID, 
                                           StringValue = StringValue
                                      };

        if(ID.HasValue && DateTime.HasValue)
            return new DateTimeResponse{ID = ID, DateTime = DateTime};

        //default case; success and child data with optional affectedRows
        return new CompoundResponse{
                                       success = success, 
                                       affectedRows = affectedRows, // can be null 
                                       data = data.Select(d=>d.ToResponse())
                                                 .ToArray()
                                   };            
    }
}

Obviously you'll need objects similar to the ones you already have, all derived from a common "response".

The last hurdle will be knowing the specific type of any given element. For that, I recommend a "discriminator"; a common property providing an easy, unique type identifier:

public abstract class Response
{
    public string ReponseTypeName{ get { return GetType().Name; } }
}
Community
  • 1
  • 1
KeithS
  • 70,210
  • 21
  • 112
  • 164
  • Although I would prefer to deserialize the entire JSON object, it is worth mentioning that all I really need in this case is `SomeOtherID`. Also, I'm considering using dynamic, but I'm still trying to figure out exactly how to use that. – Jared Price Oct 23 '14 at 20:41
  • Maybe you should start from your backend and rethink the structure of your JSON response. If you can change it, good, because this seems way too complex. – Mikko Viitala Oct 26 '14 at 14:18