2

I'm currently working on a .NET client for the Zoho Books API in order to integrate some data within my organization. I have been struggling with deserializing the json returned by the API. The problem has been because of inheritance and varying property names. I think I'm looking at creating a custom converter in order to avoid creating response types for every model for the sake of a few varying fields.

I was able to create a custom converter that works for parent responses which look like this:

{
     "code" : 0,
     "message" : "success",
     "invoice" : { // This field varies by model
         "invoice_id" : "..."
     }
}

I have created a gist for this custom converter.

One of the issues with the custom converter is that when I pass the generic return type down from the web client, I need it in either in the base response format or the paginated response format, and generics are failing me here. My converter doesn't work for paginated responses.

I defined my base type of ZohoBooks Response as so:

namespace ZohoBooks4Net.Responses
{
    public class ZohoBooksResponse<T> : IZohoBooksResponse<T>
    {
        /// <summary>
        /// Zoho Books error code. This will be zero for a success response and non-zero in case of an error.
        /// </summary>

        [JsonProperty("code")]
        public int Code { get; set; }

        /// <summary>
        /// Message for the invoked API.
        /// </summary>

        [JsonProperty("message")]
        public string Message { get; set; }

        /// <summary>
        /// Comprises the invoked API’s Data.
        /// </summary>
        public T Resource { get; set; }
    }
}

Resource is what I'm calling the third field in the response.

However, when the response returns as paginated, it adds another field.

{
  "code": 0,
  "message": "success",
  "contacts": [
    { "..." }
  ],
  "page_context": {
    "page": 1,
    "per_page": 200,
    "has_more_page": false,
    "applied_filter": "Status.All",
    "sort_column": "contact_name",
    "sort_order": "D"
  }
}

So I created this object that describes it:

namespace ZohoBooks4Net.Responses.PaginatedResponses
{
    public class PaginatedResponse<T> : ZohoBooksResponse<T>, IPaginatedResponse<T>
    {
        [JsonProperty("page_context")]
        public PageContext Context { get; set; }
    }

    public class PageContext
    {
        [JsonProperty("page")]
        public int Page { get; set; }

        [JsonProperty("per_page")]
        public int PerPage { get; set; }

        [JsonProperty("has_more_page")]
        public bool HasMorePage { get; set; }
    }
}

If anyone has any solutions I would really appreciate it.

  • It is not easier to flatten out your model at server side and give client side an flattened model? – AndrasCsanyi Sep 09 '17 at 17:29
  • I'm not a Zoho employee. If it were up to me, I would have. Some of the fields they give (like code and message) could be in the headers instead. – Brandon James Sep 09 '17 at 17:37
  • 1
    Would this solve your problem? [How to change property names depending on the type when serializing with Json.net?](https://stackoverflow.com/q/44577850). – dbc Sep 09 '17 at 17:53
  • @dbc I found out that converter is for writing, not reading. – Brandon James Sep 09 '17 at 18:22
  • @dbc Using the resource from your comment, I implemented the ReadJson method in the converter. I'll also put it up as a gist in case someone wants to make it work better. I also changed it to suit my needs by changing the property to a generic class instead of an object. I'll give you the answer so you can get credit for the comment. Thanks for the lead! [DynamicPropertyConverter](https://gist.github.com/nla-brandonjames/a0b30804c82e6a381cecf1b20f624b08) – Brandon James Sep 09 '17 at 19:57
  • @BrandonJames - Since you improved that answer by implementing reading, I'd suggest you go ahead and [answer your own question](https://stackoverflow.com/help/self-answer). All I did was to find a link. – dbc Sep 11 '17 at 21:55

3 Answers3

1

Books already hosted .Net library in github. In Subscription java client, page context and resources handled separately.

Suresh
  • 1,063
  • 1
  • 11
  • 16
  • 1
    I had been searching GitHub for this! Thanks for showing it to me. However I am actually looking for one implementing .NET Standard, so it works on any .NET project (we use Linux & .NET Core and Xamarin projects). I see now that Zoho has a GitHub account. I'll have to look at the code if I need some inspiration. – Brandon James Sep 10 '17 at 16:43
0

Have you tried using the json2csharp web application tool? It will create the classes needed from your JSON response and a root object which can be used with the Newtonsoft JsonCovnert's DeserializeObject method.

Rnice4christ
  • 78
  • 2
  • 4
  • I actually prefer http://jsonutils.com/. It suits my needs better. What I'm trying to do here is have a base class for just responses, and separate the models that the responses return, because every response is going to have the same redundant properties (code and message). Those properties aren't relevant to the model itself. – Brandon James Sep 09 '17 at 17:43
0

Taking a referenced post from a commenter, I implemented the ReadJson method created by poster of the article. This solved my issue. Here's a link to DynamicPropertyNameConverter on Github Gist.