Reputation: 1170
I'm using the Json.Net library (for .NET v3.5) to handle deserialisation of responses from the EBoss CRM API. As far as I've seen the API documentation is rather spotty, and so I'm having to poke at it myself to see what kind of responses can be elicited.
I wanted to have a single wrapper class EBossApiResponse<T>
that would provide properties for any error messages that the API sent back, plus a property containing the deserialised data of type T
(which would be restricted to an abstract base class which acts as the underlying type of any classes I created to model the returned data).
My initial poking revealed the first problem. A valid request can return an array of objects, such as: https://ebosscrm.com/api.php/job_type.json
This will easily deserialise to a hypothetical T
of List<EBossJobType>
. Note that there's no error property in the results.
A malformed request to that same endpoint however returns something different: https://ebosscrm.com/api.php/job_type.json?search[foo]=1
In this case, an array is returned which contains a single object with a single property named message
.
(Note, there are calls for which having a parameter named search[something]
is valid, but foo
is never a valid something
)
There is also the possibility for an explicit error to be returned. It looks to me like the API is catching exceptions and formatting a JSON response containing the debug information: https://ebosscrm.com/api.php/candidates.json?uid=114&api_key=f34js3kj
In this case, the returned JSON is not an array, it's a single object. I'm not sure how to cater for these differing response structures. My initial thinking was to have something like:
protected bool IsNonDataResponse(string response)
{
JObject o = JObject.Parse(response);
return o.SelectToken("message") != null || o.SelectToken("error") != null;
}
I could use this method to then either deserialise directly to the right EBossApiResponse<T>
type (if true, populating error messages but leaving the .Data
property == null), or deserialise to the right List<EBossEntityType>
, create a new EBossApiResponse<List<EBossEntityType>>
, and set its .Data
property.
Then I realised that o.SelectToken("message")
won't work, because o
would be an array, not an object. Can .SelectToken()
take a pattern of the form "[0].message"
to get the message property of the first item?
How about this?
JObject o = JObject.Parse(response);
JArray arr = o as JArray;
return (arr != null && (JObject)arr[0].SelectToken("message") != null)
|| o.SelectToken("error") != null;
Or am I barking completely up the wrong tree, and there's a more elegant solution to this whole thing?
Upvotes: 0
Views: 2035
Reputation: 6183
You could at least replace JObject.Parse()
with JToken.Parse()
because JToken
is the base class for both JObject
and JArray
. Then you can check whether the result is either a JArray
or a JObject
(or maybe a JValue
like an integer).
var result = JToken.Parse(response);
if (result is JArray)
{
// ...
}
else if (result is JObject)
{
// ...
}
Have also a look at the documentation for JToken.
Upvotes: 9