Reputation: 3547
I have a JSON file where the temperature
is sometimes a number (ver. 1) and sometimes an object (ver. 2):
// ver. 1
{
"lat": 44.47,
"lon": 11.43,
"timezone": "Europe/Rome",
"temperature": 4.14,
}
// ver. 2
{
"lat": 44.47,
"lon": 11.43,
"timezone": "Europe/Rome",
"temperature": {
"min": 1.1,
"max": 12.3
}
}
To represent these two 'almost equal' formats I'd like to have only one class with (possibly?) a custom converter for the temperature
. All the other fields will be converted automatically:
public class Forecast {
private Coordinates _coord = new Coordinates();
private Temperature _temperature = new Temperature();
// json.net will do everything to convert 'lat' in a double
[JsonProperty("lat")]
public double Latitude {
get => _coord.Latitude;
set => _coord.Latitude = value;
}
// json.net will do everything to convert 'lon' in a double
[JsonProperty("lon")]
public double Longitude {
get => _coord.Longitude;
set => _coord.Longitude = value;
}
// json.net will do everything to convert 'timezone' in a string
[JsonProperty("timezone")]
public string TimeZone{ get; set; }
// HERE I WANT TO USE A CUSTOM CONVERTER THAT HANDLES the 2 VERSIONS of 'temp' IN THE PROPER WAY.
// THE CODE BELOW DOES NOT WORK AT ALL!
[JsonProperty("temperature", ItemConverterType = typeof(MyTemperatureConverter))]
public Object Temp {
get => _temperature; // return a Temperature object
set {
// do something here to handle it? E.g.:
if (value is Double) {
// it is a ver. 1
_temperature.Avg = (value as double);
} else {
// it is a ver. 2
_temperature.Max = (value as Temperature).Max;
_temperature.Min = (value as Temperature).Min;
}
}
}
}
public class Coordinates {
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public class Temperature {
public double? Avg { get; set; }
public double? Min { get; set; }
public double? Max { get; set; }
}
Here is my (hypothetical) deserializer:
public class MyTemperatureConverter : JsonConverter {
public override void WriteJson(JsonWriter writer, [AllowNull] object value, JsonSerializer serializer) {
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) {
// WHAT TO DO HERE?
}
}
And here is my general code to read the JSON:
protected virtual async Task<Forecast> ParseForecastFromResponse(HttpResponseMessage response) {
using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) {
using (var jsonReader = new Newtonsoft.Json.JsonTextReader(new System.IO.StreamReader(responseStream))) {
var serializer = new Newtonsoft.Json.JsonSerializer();
return serializer.Deserialize<Forecast>(jsonReader);
}
}
}
The question is: am I on the right track? Or do I need to go in a different direction?
Upvotes: 1
Views: 149
Reputation: 129777
You can make a converter to handle both formats like this:
public class MyTemperatureConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Temperature);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
Temperature temp = new Temperature();
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Object)
{
temp.Min = (double?)token["min"];
temp.Max = (double?)token["max"];
}
else
{
temp.Avg = (double?)token;
}
return temp;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
In your Forecast
class, the Temperature
property should be defined and annotated like this:
[JsonProperty("temperature"), JsonConverter(typeof(MyTemperatureConverter))]
public Temperature Temperature { get; set; } = new Temperature();
Here is a working demo: https://dotnetfiddle.net/3yrUU3
Upvotes: 2