Search code examples
c#json.net

Deserialize JSON with same answer property name but differents properties inside


I'm implementing an external API who give me a response like this if is OK:

{
    status: "success",
    answer: {
        token: "tokenGenerated..."
    }
}

Or like this if I get an error:

{
    status: "ERROR",
    answer: {
        errorCode: "error code"
        errorMessage: "error message"
    }
}

I'm trying to deserialize the response in two differents class, one for the OK response, and another for the ERROR response. I'm doing something like this:

public class Response
{
    [JsonProperty("status")]
    private string Status { get; set; }
    public bool IsSuccess => Status != "ERROR";
    
    [JsonProperty("answer")]
    public object Response { get; set; }
}

public class ResponseError : Response
{
    [JsonProperty("errorCode")]
    public string ErrorCode { get; set; }

    [JsonProperty("errorMessage")]
    public string ErrorMessage { get; set; }
}

public class ResponseOk : Response
{
    [JsonProperty("token")]
    public string Token { get; set; }
}

And when I get the response I do this:

var readResponse = await response.Content.ReadAsStringAsync();                    
var deserializedResponse = JsonConvert.DeserializeObject<Response>(readResponse);
if (deserializedResponse.IsSuccess)
    return JsonConvert.DeserializeObject<ResponseOk>(readResponse);
else
    return JsonConvert.DeserializeObject<ResponseError>(readResponse);

In my method I return a object. My problem is, for example, if I get an OK status, the property Status is well implemented, and in the Response property I get the "token: "tokenhere"", not in the Token property.

And if I try to put inside the Resposne class the Token property and the errors properties, I need to apply the JsonProperty("answer") in both cases and of course I get an exception.

There's a better way of implementing this for my case? Thx.


Solution

  • You map answer object in JSON to Response property in Response class.

    Having this class:

    public class ResponseOk : Response
    {
        [JsonProperty("token")]
        public string Token { get; set; }
    }
    

    you expect a token property in JSON as root property, but you have token inside answer so this mapping won't work.

    You will have the same issue when you get an error - ErrorCode and ErrorMessage also won't be set because you map them to root propeties.

    You can change your classes to:

    public class Response
    {
        [JsonProperty("status")]
        private string Status { get; set; }
        public bool IsSuccess => Status != "ERROR";
        
        [JsonProperty("answer")]
        public Answer Response { get; set; } // can be just renamed to "Answer"
    }
    
    public class Answer
    {
        [JsonProperty("errorCode")]
        public string ErrorCode { get; set; }
    
        [JsonProperty("errorMessage")]
        public string ErrorMessage { get; set; }
    
        [JsonProperty("token")]
        public string Token { get; set; }
    }
    

    And now you can check the IsSuccess in Response class and decide what to use - Token or error properties.