问题
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:
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
来源:https://stackoverflow.com/questions/53326193/how-to-custom-deserialize-into-an-object-with-json-net