Mrg Gek
Mrg Gek

Reputation: 936

Can I add additional type that will combine multiple json properties when deserializing JSON?

The original JSON C# class structure looks like

 public class Main
    {
        public double temp { get; set; }
        public double temp_min { get; set; }
        public double temp_max { get; set; }
        public double pressure { get; set; }
        public double sea_level { get; set; }
        public double grnd_level { get; set; }
        public int humidity { get; set; }
        public double temp_kf { get; set; }
    }

    public class Weather
    {
        public int id { get; set; }
        public string main { get; set; }
        public string description { get; set; }
        public string icon { get; set; }
    }

    public class Clouds
    {
        public int all { get; set; }
    }

    public class Wind
    {
        public double speed { get; set; }
        public double deg { get; set; }
    }

    public class Snow
    {
        public double? 3h { get; set; }
    }

    public class Sys
    {
        public string pod { get; set; }
    }

    public class Rain
    {
        public double 3h { get; set; }
    }

    public class List
    {
        public int dt { get; set; }
        public Main main { get; set; }
        public IList<Weather> weather { get; set; }
        public Clouds clouds { get; set; }
        public Wind wind { get; set; }
        public Snow snow { get; set; }
        public Sys sys { get; set; }
        public string dt_txt { get; set; }
        public Rain rain { get; set; }
    }

    public class Coord
    {
        public double lat { get; set; }
        public double lon { get; set; }
    }

    public class City
    {
        public int id { get; set; }
        public string name { get; set; }
        public Coord coord { get; set; }
        public string country { get; set; }
    }

    public class Example
    {
        public string cod { get; set; }
        public double message { get; set; }
        public int cnt { get; set; }
        public IList<List> list { get; set; }
        public City city { get; set; }
    }

I want to remove some properties and merge other into my types, so at the end I want to get next C# class structure:

public class WeatherForecast
    {
        [JsonProperty("city")]
        public City City { get; set; }

        [JsonProperty("list")]
        public IList<ClimateIndicators> ClimateIndicators { get; set; }
    }

 public class ClimateIndicators
    {
        public Atmospheric Atmospheric { get; set; }
        public Hydrospheric Hydrospheric { get; set; }
        public Lithospheric Lithospheric { get; set; }
        public DateTime Date { get; set; }

        public IList<WeatherDetails> WeatherDetails { get; set; }
    }

 public class Atmospheric
    {
        [JsonProperty("pressure")]
        public double Pressure { get; set; }
        [JsonProperty("humidity")]
        public int Humidity { get; set; }
        [JsonProperty("sea_level")]
        public double SeaLevel { get; set; }
        [JsonProperty("grnd_level")]
        public double GroundLevel { get; set; }
        [JsonProperty("all")]
        public int Cloudiness { get; set; }
        [JsonProperty("rain")]
        public double? Rain { get; set; }
        [JsonProperty("snow")]
        public double? Snow { get; set; } 
    }

I am adding custom classification for the weather conditions. So the problem I am facing is that I don't know how to merge multiple JSON Properties into a single class that is a property of another JSON property.

Upvotes: 0

Views: 293

Answers (1)

Brian Rogers
Brian Rogers

Reputation: 129787

Since the class structure you want has a different shape than the JSON, you will need to create a custom JsonConverter to bridge the differences. In your case, the WeatherForecast and City classes don't need any special handling (just using the [JsonProperty] attributes is sufficient), but the ClimateIndicators class definitely needs a converter.

Here is what it would look like in code. The converter works by loading the JSON data for each item of the list array into a temporary JObject. (Json.Net calls ReadJson once for each item, so that is why there is no loop in the converter.) From there, SelectToken is used to pick out the values from the various nested item properties needed to populate an instance of the ClimateIndicators class and its supporting class Atmospheric. The ClimateIndicators instance is then returned to Json.Net, which automatically adds it to the list in the WeatherForecast class.

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = JObject.Load(reader);

        Atmospheric atm = new Atmospheric();
        atm.Pressure = (double)obj.SelectToken("main.pressure");
        atm.Humidity = (int)obj.SelectToken("main.humidity");
        atm.SeaLevel = (double)obj.SelectToken("main.sea_level");
        atm.GroundLevel = (double)obj.SelectToken("main.grnd_level");
        atm.Cloudiness = (int)obj.SelectToken("clouds.all");
        atm.Rain = (double?)obj.SelectToken("rain.3h");
        atm.Snow = (double?)obj.SelectToken("snow.3h");

        ClimateIndicators indicators = new ClimateIndicators();
        indicators.Atmospheric = atm;
        indicators.Date = (DateTime)obj.SelectToken("dt_txt");

        return indicators;
    }

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

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

To use the converter, add a [JsonConverter] attribute to the ClimateIndicators class like this:

    [JsonConverter(typeof(ClimateIndicatorsConverter))]
    public class ClimateIndicators
    {
        ...
    }

Note that you do not need [JsonProperty] attributes on the properties within the ClimateIndicators or Atmospheric class, because the property mapping is being handled by the converter.

Here is a working demo: https://dotnetfiddle.net/Fc9G69

Upvotes: 2

Related Questions