Reputation: 127
I have a service that consumes API methods and it returns a json string. I have a base response object below:
public class BaseResponse
{
public bool HasError { get; set; }
public string Message { get; set; }
}
and a generic type parser
Task<R> ParseResponse<R>(HttpResponseMessage response) where R : BaseResponse, new()
That returns:
return JsonConvert.DeserializeObject<R>(responseText);
Note that I don't have control over the response of the api. So I get this response, an array of objects.
[{"created":"0001-01-01T00:00:00","updated":null,"name":"Test 1","decscription":null,"id":1},{"created":"0001-01-01T00:00:00","updated":null,"name":"Test 2","decscription":null,"id":2},{"created":"0001-01-01T00:00:00","updated":null,"name":"Test 3","decscription":null,"id":3},{"created":"0001-01-01T00:00:00","updated":null,"name":"Test 4","decscription":null,"id":4}]
The problem is I would like to parse it to this class:
public class TestResponse : BaseResponse
{
public Test[] Tests { get; set; }
}
The HasError
and Message
will have a value when an error occurred on the server or the api returned a Bad Request etc.
Any information will be appreciated. Thank you.
Upvotes: 1
Views: 290
Reputation: 117046
From your comment, it sounds as though a successful response will appear as a JSON array, but if an error occurs then a JSON object will be returned with an error flag and message. In order to parse both possible responses, you can create a custom JsonConverter
that checks whether the received JSON is an array or an object, and proceeds accordingly:
public class TestResponseConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(TestResponse);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
else if (reader.TokenType == JsonToken.StartArray)
{
// A valid response. Populare the Tests array.
var tests = serializer.Deserialize<Test[]>(reader);
return new TestResponse { Tests = tests };
}
else if (reader.TokenType == JsonToken.StartObject)
{
// An error response. Populate HasError and Message
var response = new TestResponse();
serializer.Populate(reader, response);
return response;
}
else
{
throw new JsonSerializationException("Unexpected reader.TokenType: " + reader.TokenType);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var test = (TestResponse)value;
if (test.HasError && test.Tests != null && test.Tests.Length > 0)
{
serializer.Serialize(writer, new { HasError = test.HasError, Message = test.Message, Tests = test.Tests });
}
else if (test.HasError || test.Tests == null)
{
serializer.Serialize(writer, new { HasError = test.HasError, Message = test.Message });
}
else
{
serializer.Serialize(writer, test.Tests);
}
}
}
Then apply it to the class using JsonConverterAttribute
like so:
[JsonConverter(typeof(TestResponseConverter))]
public class TestResponse : BaseResponse
{
public Test[] Tests { get; set; }
}
Or add it to JsonSerializerSettings
like so:
var settings = new JsonSerializerSettings { Converters = new[] { new TestResponseConverter() } };
var response = JsonConvert.DeserializeObject<R>(responseText, settings);
Prototype fiddle.
Upvotes: 1