0

I'm doing some web API integration with Newtonsoft.Json, and as always, I have to do dumb stunts to properly deserialize what they're sending back.

In this case, the API will send responses resembling this kind of structure:

{ "contacts": [ ... ], "has-more": true, "offset": 38817 }

The "has-more" and "offset" properties are pretty much constant on the different method responses, and have been defined accordingly on the response object that I'm deserializing into. The response object looks something like this:

public class APIResponse {
    public JContainer Result { get; set; }
    [JsonProperty("has-more")]
    public bool HasMore { get; set; }
    [JsonProperty("offset")]
    public int Offset { get; set; }
}

That first "contacts" property is what can vary; for some methods, I might get "contacts", some might get "companies", and others might get who-knows-what. I also don't have any way to be certain that every response will have such a "variable" property, nor that it will be the first one, positionally speaking.

For this example, what I would like to happen is the deserializer looks at the Json and says, "Let's see, I don't see anything mapping to 'contacts', so we'll put that into 'Result', and then I can see from the JsonProperty attributes that 'has-more' and 'offset' go into HasMore and Offset. Okay, all set, here's your object."

I suspect this involves some tricks with a custom JsonConverter or IContractResolver, but I'm just not connecting the dots here. I tried doing a simple custom contract resolver, but it appears to use contract resolvers to resolve object property names into property names to look for in the JSON text, not vice-versa.

db2
  • 497
  • 1
  • 3
  • 21
  • 1
    You should be able to use [`[JsonExtensionData]`](http://www.newtonsoft.com/json/help/html/DeserializeExtensionData.htm). – dbc Sep 30 '16 at 15:49
  • @dbc That definitely looks like it could get the job done. I'll try it out this afternoon. – db2 Sep 30 '16 at 15:52

1 Answers1

1

You can use a base class + derivations for each response type.

public class APIResponseBase {
    [JsonProperty("has-more")]
    public bool HasMore { get; set; }
    [JsonProperty("offset")]
    public int Offset { get; set; }
}

public class ContactsResponse : APIResponseBase {
  public IEnumerable<Contact> Contacts { get; set; }
}

public class CompaniesResponse : APIResponseBase {
  public IEnumerable<Company> Companies { get; set; }
}

var contactsResponse = JsonConvert.Deserialize<ContactsResponse>(json);
IEnumerable<Contact> contacts = contactsResponse.Contacts
Jeff
  • 12,085
  • 12
  • 82
  • 152
  • That seems like a decent idea. I imagine I could turn the `Execute()` method that makes the actual API call into a generic that takes the type of response subclass to deserialize into. – db2 Sep 30 '16 at 19:04
  • You got it. ;) generics are powerful – Jeff Sep 30 '16 at 19:05