Lucas Perrett Lucas Perrett - 27 days ago 27
C# Question

Deserialize Json Web API Response - Nested objects

I am getting a Web API JSON Response via SSIS Script component, that is coming back in the following format:

{
"Success": true,
"Response": {
"Applications": [
{
"App_ID": 1638486,
"App_Ref": "Test Example",
"Status": "Complete",
"Error_Code": null,
"Error_Message": null,
"Create_Dt": "2014-05-14 03:09:01.030 +00:00",
"Modify_Dt": "2014-05-14 03:10:59.757 +00:00",
"Client_Name": "Silver Chef",
"Client_Code": "SLVC01",
"Centrelink_Status": "Receiving_Logons"
},
{
"App_ID": 1637906,
"App_Ref": "SME Demo",
"Status": "Complete",
"Error_Code": null,
"Error_Message": null,
"Create_Dt": "2015-10-08 03:07:26.793 +00:00",
"Modify_Dt": "2015-10-08 03:23:32.833 +00:00",
"Client_Name": "Silver Chef",
"Client_Code": "SLVC01",
"Centrelink_Status": "Receiving_Logons"
},
{
"App_ID": 1585286,
"App_Ref": "Test",
"Status": "Receiving_Logons",
"Error_Code": null,
"Error_Message": null,
"Create_Dt": "2015-12-04 03:12:49.617 +00:00",
"Modify_Dt": "2015-12-04 03:12:49.617 +00:00",
"Client_Name": "Silver Chef",
"Client_Code": "SLVC01",
"Centrelink_Status": "Receiving_Logons"
}
],
"Current_Dt": "2016-11-11 01:01:01.743 +00:00",
"Last_App_Dt": "2016-10-03 22:48:56.397 +00:00",
"Count": 500,
"Total": 1870
}
}


I have declared the following classes:

public class Application
{
public int App_ID { get; set; }
public string App_Ref { get; set; }
public string Status { get; set; }
public int Error_Code { get; set; }
public string Error_Message { get; set; }
public DateTime Create_Dt { get; set; }
public DateTime Modify_Dt { get; set; }
public string Client_Name { get; set; }
public string Client_Code { get; set; }
public int Centrelink_Status { get; set; }

}
public class Response
{
public List<Application> Applications { get; set; }
public string Current_Dt { get; set; }
public string Last_App_Dt { get; set; }
public int Count { get; set; }
public int Total { get; set; }
}

public class RootObject
{
public bool Success { get; set; }
public Response Response { get; set; }
}

And this is the method that I am using to get the response.
private RootObject GetWebServiceResult(string vAPIUrl)
{

string vAPIToken = Variables.APIToken;

//Create Web Request
HttpWebRequest apireq = (HttpWebRequest)WebRequest.Create(vAPIUrl);
apireq.ContentType = "application/json";
apireq.Method = "POST";

string jsonPostStr = "{\"Settings\": {\"API_Token\": \"" + vAPIToken + "\"}, \"Payload\": {}}";
byte[] postString = Encoding.UTF8.GetBytes(jsonPostStr);

apireq.ContentLength = postString.Length;

Stream jsonStream = apireq.GetRequestStream();

jsonStream.Write(postString, 0, postString.Length);
jsonStream.Close();

// Get Web Response
HttpWebResponse apirsp = (HttpWebResponse)apireq.GetResponse();
RootObject jsonResponse = null;

Stream jsonRspStream = apirsp.GetResponseStream();
string apiResponseString = null;

using (StreamReader reader = new StreamReader(jsonRspStream))
{
apiResponseString = reader.ReadToEnd();
Console.WriteLine(apiResponseString);
reader.Close();
}


JavaScriptSerializer returnJson = new JavaScriptSerializer();

//var serialJsonStr = returnJson.Serialize(apiResponseString);
System.Windows.Forms.MessageBox.Show(apiResponseString);

jsonResponse = returnJson.Deserialize<RootObject>(apiResponseString);

return jsonResponse;

}


My issue is that returnJson.Deserialize(apiResponseString); seems to return null and subsequently makes this fail.

What am i missing? i think i am slightly code blind at this stage.

Thanks in advance

dbc dbc
Answer

There are a couple of problems with the Application class:

  • You have defined Application.Error_Code to be an integer:

    public int Error_Code { get; set; }
    

    But in fact, in the JSON, it has a null value:

    "Error_Code": null,
    

    Thus Error_Code needs to be a reference type (string) or nullable value type (int?) depending on what it holds when non-null.

  • You have defined Centrelink_Status as an int, but in the JSON it's a string:

    "Centrelink_Status": "Receiving_Logons"
    

    Thus it needs to be a string in the data model as well.

Thus your Application class should look like:

public class Application
{
    public int App_ID { get; set; }
    public string App_Ref { get; set; }
    public string Status { get; set; }
    public int? Error_Code { get; set; } // Or string, if not numeric
    public string Error_Message { get; set; }
    public DateTime Create_Dt { get; set; }
    public DateTime Modify_Dt { get; set; }
    public string Client_Name { get; set; }
    public string Client_Code { get; set; }
    public string Centrelink_Status { get; set; }
}

You might also want to make those DateTime properties be nullable if there is a chance for a null value in the JSON.

Incidentally, when I tried to deserialize this JSON, I got an exception thrown, rather than a null value returned:

System.InvalidOperationException was unhandled
  Message="Cannot convert null to a value type."
  Source="System.Web.Extensions"
  StackTrace:

In your actual code, are you catching and swallowing exceptions? That's a bad idea precisely because it tends to hide errors such as this. At the minimum, when debugging you should break whenever any exception is thrown including a first-chance exception - even though this is not the default behavior. See Managing Exceptions with the Debugger. If you find there are certain types of exception that are frequently thrown and are irrelevant to debugging, you can selectively re-ignore them. Had you done so this bug would have been more straightforward to track down. Admittedly, the error is not very explanatory, but it does give a hint. If you were to use to deserialize your JSON, the error message would be much clearer:

Newtonsoft.Json.JsonSerializationException occurred
  Message="Error converting value {null} to type 'System.Int32'. Path 'Response.Applications[0].Error_Code', line 9, position 26."
  Source="Newtonsoft.Json"
  StackTrace:
       at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType) in C:\Development\Releases\Json\Working\Newtonsoft.Json\Working-Signed\Src\Newtonsoft.Json\Serialization\JsonSerializerInternalReader.cs:line 986
  InnerException: System.InvalidCastException
       Message="Null object cannot be converted to a value type."
       Source="mscorlib"
       StackTrace:
            at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
            at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType) in C:\Development\Releases\Json\Working\Newtonsoft.Json\Working-Signed\Src\Newtonsoft.Json\Serialization\JsonSerializerInternalReader.cs:line 979
       InnerException: