Search code examples
c#vb.netjson.netnest-api

Deserializing JSON from Nest API into C# objects


Trying to connect my custom .NET automation system to my Nest Thermostat. I'm having a problem deserializing the JSON data and not all of populates into the classes. I am not very versed with JSON or the NEST API so I hope I am approaching this correctly. I am using Newtonsoft's JSON library.

I started from the top and created a class that represents all the metadata.

public class All
{
    public Dictionary<string, Devices> devices { get; set; }
    public Dictionary<string, Structures> structures { get; set; }
}

I then created Structures Class

public class Structures
{
    public Dictionary<string, Structure> structure { get; set; }
}

and Devices class

public class Devices
{
    public Dictionary<string, Thermostats> thermostats { get; set; }
}

Also created the Structure

public class Structure
{
    public string name { get; set; }
    public string country_code { get; set; }
    public string time_zone { get; set; }
    public string away { get; set; }
    public IList<string> thermostats { get; set; }
    public string structure_id { get; set; }
}

and Thermostats

public class Thermostats
{
    public Dictionary<string, ThermostatDetails> thermostatdetails { get; set; }
}

and finally Thermostatdetails

public class ThermostatDetails
{
    public int humidity { get; set; }
    public string locale { get; set; }
    public string temperature_scale { get; set; }
    public bool is_using_emergency_heat { get; set; }
    public bool has_fan { get; set; }
    public string software_version { get; set; }
    public bool has_leaf { get; set; }
    public string device_id { get; set; }
    public string name { get; set; }
    public bool can_heat { get; set; }
    public bool can_cool { get; set; }
    public string hvac_mode { get; set; }
    public double target_temperature_c { get; set; }
    public int target_temperature_f { get; set; }
    public double target_temperature_high_c { get; set; }
    public int target_temperature_high_f { get; set; }
    public double target_temperature_low_c { get; set; }
    public int target_temperature_low_f { get; set; }
    public double ambient_temperature_c { get; set; }
    public int ambient_temperature_f { get; set; }
    public double away_temperature_high_c { get; set; }
    public int away_temperature_high_f { get; set; }
    public double away_temperature_low_c { get; set; }
    public int away_temperature_low_f { get; set; }
    public string structure_id { get; set; }
    public bool fan_timer_active { get; set; }
    public string name_long { get; set; }
    public bool is_online { get; set; }
    public DateTime last_connection { get; set; }
}

Once I've made the API call for All metadata and get the JSON back I am trying to deserialize with

Dim deserializedProduct As Nest.All = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Nest.All)(rawresp)

It does not throw any errors and seems to process fine. In my case I only have 1 thermostat. After it runs I see deserializedProduct.devices contains 1 and deserialize.structures contains 1.

If I look at deserialize.structures(0) it has the structure key and the value shows as an empty Nest.Structure object meaning that it has no properties.

If I look at deserializedProduct.devices it shows Thermostats class but no additional data below that.

Has anyone been able to get such an approach working? Is the problem deserializing nested JSON?

Any help or guidance is most appreciated.


Solution

  • You didn't show the JSON you're trying to deserialize, so I'm going to assume it is what is shown on this API reference page.

    The things to keep in mind when you create your classes are:

    1. The property names in your classes need to match the property names in the JSON at the same level, or else be decorated with a [JsonProperty] attribute to specify the JSON property name.
    2. Where the JSON property names can vary (i.e. they are IDs), at that point you want to use a Dictionary<string, T> where T is the target object type.
    3. If you are not interested in a particular piece of data, you can simply omit that property from your class, and it will be ignored by default.

    Given all that, you're not too far off; it seems that you just have a few too many levels of dictionaries and so the resulting structure doesn't match the JSON. Here is all you really need to deserialize the devices and structures:

        public class All
        {
            public Devices devices { get; set; }
            public Dictionary<string, Structure> structures { get; set; }
        }
    
        public class Devices
        {
            public Dictionary<string, Thermostat> thermostats { get; set; }
        }
    
        public class Structure
        {
            public string name { get; set; }
            public string country_code { get; set; }
            public string time_zone { get; set; }
            public string away { get; set; }
            public IList<string> thermostats { get; set; }
            public string structure_id { get; set; }
        }
    
        public class Thermostat
        {
            public int humidity { get; set; }
            public string locale { get; set; }
            public string temperature_scale { get; set; }
            public bool is_using_emergency_heat { get; set; }
            public bool has_fan { get; set; }
            public string software_version { get; set; }
            public bool has_leaf { get; set; }
            public string device_id { get; set; }
            public string name { get; set; }
            public bool can_heat { get; set; }
            public bool can_cool { get; set; }
            public string hvac_mode { get; set; }
            public double target_temperature_c { get; set; }
            public int target_temperature_f { get; set; }
            public double target_temperature_high_c { get; set; }
            public int target_temperature_high_f { get; set; }
            public double target_temperature_low_c { get; set; }
            public int target_temperature_low_f { get; set; }
            public double ambient_temperature_c { get; set; }
            public int ambient_temperature_f { get; set; }
            public double away_temperature_high_c { get; set; }
            public int away_temperature_high_f { get; set; }
            public double away_temperature_low_c { get; set; }
            public int away_temperature_low_f { get; set; }
            public string structure_id { get; set; }
            public bool fan_timer_active { get; set; }
            public string name_long { get; set; }
            public bool is_online { get; set; }
            public DateTime last_connection { get; set; }
        }
    

    Fiddle: https://dotnetfiddle.net/7wsRF1