1

I used http://json2csharp.com/ to generate a csharp class that represents my JSON, which worked wonderfully. However, after working with the third party api for a while, I realized that their responses aren't always sending the same type of data. I'm querying their API for Ticket data, and each ticket has a SellPrice. Sometimes they return that SellPrice as a Money object that has a currency (string) and amount (double), and sometimes they send it back as just a double. So I'm trying to find a way to handle this gracefully, so that I always set the Amount of my SellPrice object. Here's a short version of the code that works when they send a Money object as SellPrice

public class Ticket
{
    public string Ticket_Id {get; set;}
    //... other fields
    public SellPrice SellPrice {get; set;}
}
public class SellPrice
{
    public string Currency {get; set;}
    public double Amount {get; set;}
}

And then when I get the Json, I deserialize like so, which works great...

for (int i = 0; i < jItems.Count; i++)
{
    TUTicket2 item = jItems[i].ToObject<TUTicket2>();
}

...until I run into an API call that returns a double instead of an object.

So just looking at the case where it's Money object, I thought I'd try to create a constructor so that I can set the values depending on the object type, like so:

public class SellPrice
{
    public SellPrice(object sellPrice)
    {
        if (sellPrice.GetType() == typeof(Dictionary<string, object>))
        {
            Currency = (string)((Dictionary<string, object>)sellPrice)["Currency"];
            Amount = (double)((Dictionary<string, object>)sellPrice)["Currency"];
        }
    }
    public string Currency { get; set; }
    public double Amount { get; set; }
}

But that doesn't work because the sellPrice object is always null, so I think I'm barking up the wrong tree there. Is there a way to do this easily? I think my problem is the way it auto-deserializes to an object type, but I've been looking through the code/documentation and haven't figured out what I'm missing.

I have a couple goals here: We are doing enough API work that I'd like to be able to utilize a tool like json2csharp to generate the classes. I'd also like to avoid doing manual deserialization for every class/object, although if that's my only option, I can go in that direction, it just feels like overkill when 99% of the values behave normally. I also don't want to end up with X different versions of each of my classes depending on which API call I make. I'm trying to find some solution that just lets me override a small portion instead of everything. Any feedback would be appreciated.

1 Answer 1

1

You can handle this by making a custom JsonConverter for your SellPrice class like this:

public class SellPriceConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(SellPrice);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        SellPrice sellPrice = new SellPrice();
        if (token.Type == JTokenType.Object)
        {
            serializer.Populate(token.CreateReader(), sellPrice);
        }
        else if (token.Type == JTokenType.Float)
        {
            sellPrice.Amount = (double)token;
            // if there is a default currency, set it here, e.g.:
            // sellPrice.Currency = "USD";
        }
        else
        {
            throw new JsonException("Unexpected token type for SellPrice: " + token.Type.ToString());
        }
        return sellPrice;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

To use it, all you would need to do is add a [JsonConverter] attribute to the SellPrice class like this:

[JsonConverter(typeof(SellPriceConverter))]
public class SellPrice 
{
    ...
}

Then you can just deserialize as normal and it should handle both situations.

Working demo here: https://dotnetfiddle.net/qZekyp

Sign up to request clarification or add additional context in comments.

1 Comment

I was hoping there was something more simple, but I had a feeling this was the direction I would need to go. Thanks a lot for the concrete solution as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.