Search code examples
c#generic-collections

C# populate dictionary value


I'm new to C# and I face a problem that I couldn't resolve.

I listen to my Xiaomi gateway to get information. This information comes as two types, report or heartbeat.

Report is when something trigger the sensor, as for plug, when you turn it on or off:

{"cmd":"report","model":"plug","sid":"158d000123f0c9","short_id":11119,"data":"{"status":"off"}"}

Heartbeat is sent every x minutes to say sensor is still there:

{"cmd":"heartbeat","model":"plug","sid":"158d000123f0c9","short_id":11119,"data":"{"voltage":3600,"status":"on","inuse":"0","power_consumed":"48","load_power":"0.00"}"}

As you can see, report and heartbeat doesn't contain the same parameters as data.

Here is my plug class:

[XiaomiEquipement("plug")]
public class Plug
{
    public string Model { get; set; } = "plug";
    public string Sid { get; set; }
    public string Battery { get; set; } = "CR2450";
    public int BatteryLevel { get; set; }
    public PlugReport Report { get; set; }
}

[XiaomiEquipement("plug_report")]
public class PlugReport
{
    public int Voltage { get; set; }
    public string Status { get; set; }
    public int InUse { get; set; }
    public float Power_Consume { get; set; }
    public float Load_Power { get; set; }
}

Ok, when I start my app, I ask gateway for all sensor and I enter them inside a double dictionary:

dynamic Equipements = new Dictionary<string, Dictionary<string, dynamic>>();

And when I receive report or heartbeat, I search for the dictionary entry, modify it and send to my home automation system:

Type modelReportType = Assembly.GetExecutingAssembly().GetTypes().SingleOrDefault(t => t.GetCustomAttribute<Response.XiaomiEquipementAttribute>()?.Model == data.Model + "_report");
dynamic test = Equipements[data.Model][data.Sid].Report;
dynamic data = JsonConvert.DeserializeObject(Convert.ToString(data.Data), modelReportType);

If data doesn't contain all properties, it writes them as default.

What I want is that if a property doesn't exist on the report/heartbeat, use dictionary value.

For example, my dictionary contains:

{"model":"plug","sid":"158d000123f0c9","short_id":11119,"data":"{"voltage":3600,"status":"on","inuse":"1","power_consumed":"48","load_power":"3.56"}"}

And I receive:

{"cmd":"report","model":"plug","sid":"158d000123f0c9","short_id":11119,"data":"{"status":"off"}"}

I want:

{"model":"plug","sid":"158d000123f0c9","short_id":11119,"data":"{"voltage":3600,"status":"off","inuse":"1","power_consumed":"48","load_power":"3.56"}"}

And I get for the moment:

{"model":"plug","sid":"158d000123f0c9","short_id":11119,"data":"{"voltage":3600,"status":"off","inuse":"0","power_consumed":"00","load_power":"0.00"}"}

I tried PopulateObject but it didn't work:

dynamic data = JsonConvert.PopulateObject(Convert.ToString(data.Data), test);

Anyone have an idea to modify my dynamic var without touching other properties?

Edit: some reports examples:

{"cmd":"report","model":"weather.v1","sid":"158d0001a231ab","short_id":39499,"data":"{"humidity":"5262"}"}

{"cmd":"report","model":"magnet","sid":"158d000159febe","short_id":40805,"data":"{"status":"close"}"}

And for heartbeat:

{"cmd":"heartbeat","model":"sensor_ht","sid":"158d0001a2c3e9","short_id":42820,"data":"{"voltage":3015,"temperature":"2644","humidity":"5454"}"} {"cmd":"heartbeat","model":"magnet","sid":"158d000159febe","short_id":40805,"data":"{"voltage":3025,"status":"open"}"}

As I said, I don't know in advance which sensor it will be so everything must work in every case.

That's why when I receive report/heartbeat, I searched for the report class base on model name, deserialize data part to an instance of this class.


Solution

  • As for me, you shouldn't use dynamic. Instead of this, try to something like that.

    public class Data
    {
        [JsonProperty("status")]
        public string Status { get; set; }
    }
    
    public class ReportResponse
    {
        [JsonProperty("cmd")]
        public string Cmd { get; set; }
        [JsonProperty("model")]
        public string Model { get; set; }
        [JsonProperty("sid")]
        public string Sid { get; set; }
        [JsonProperty("short_id")]
        public int Short_id { get; set; }
    
        [JsonProperty("data")]
        public Dictionary<string, string> Data { get; set; }
        //public Data Data { get; set; }
    }
    
    var deserializeReponse = JsonConvert.DeserializeObject<ReportResponse>(rawResponse);
    

    When you are writing in this style, your code will be cleaner and exceptions, you're getting, will be more understandable

    P.S.

    When I need to generate objects from json, I use these two sites:

    Getting formatted json

    POCO generator