I'm new to C# and I face a problem that I couldn't resolve.
I listen to my Xiaomi gateway to get informations. These informations came as two type, 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 send 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 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 property, it write them as default.
What I want is that if a property isn't exist on the report/heartbeat, use dictionary value.
For example, my dictionary contain :
{"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 others properties ?
Edit : some reports example :
>
{"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 wich 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.
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
Related
So I am using one of the google Id API; https://developers.google.com/instance-id/reference/server#manage_registration_tokens_for_push_subscriptions
I have previously been fortunate to have API that had WDSL or similar and been easy to parse into a class.
But the JSON I got back I can not parse easily.
{
"connectDate":"2018-02-12",
"application":"com.chrome.windows",
"subtype":"wp:https://xxxxxxxxxxxxxxxxxxx",
"authorizedEntity":"xxxxxx",
"rel":{
"topics":{
"movies":{
"addDate":"2018-01-26"
},
"anotherTopic":{
"addDate":"2018-02-12"
}
}
},
"connectionType":"WIFI",
"platform":"WEBPUSH"
}
Movies and anotherTopics are the topics I created so I can't add them to my class . Or can I?
There are of course ways to do it treating the json as string and using regex or going through the node as a dynamic object (dynamic dyn = JsonConvert.DeserializeObject(content);) but ideally I think it should be a dictionary (I think at least) but can not see how.
And as this is Google I assume there is more standard way to handle this kind of JSON.
I have tried creating a dictionary which I couldn't get working.
Stepping through the nodes I can get the data but end up with code like
DateTime.Parse(((Newtonsoft.Json.Linq.JValue)((Newtonsoft.Json.Linq.JProperty)((Newtonsoft.Json.Linq.JContainer)(obj.First)).First).Value).Value.ToString())
I have tried to look for similar JSON parsing exampels but I can not find any.
I did not share my attempt to extract the data in my first edit as I don't think that is the way to do it; it is a hack.
I created a class for it
public class SubscriptionDetails
{
public DateTime connectDate { get; set; }
public string application { get; set; }
public string subtype { get; set; }
public string authorizedEntity { get; set; }
public string connectionType { get; set; }
public string platform { get; set; }
public topics rel { get; set; }
}
But I am stuck when it comes to defining the subclass topics.
So I tried
public class topics : Dictionary<string, object>
which results in one dictionary entry with key topics?
the other option requires a dictionary name
public class topics
{
public Dictionary<string, Dictionary<string, string>> DUMMY { get; set; }
}
So thanks to the suggestion from Jamiec I got on the right track.
Of course already the node rel is a dictionary with one item called topics.
So need a few classes to parse the entire thing:
public class SubscriptionDetails
{
public DateTime connectDate { get; set; }
public string application { get; set; }
public string subtype { get; set; }
public string authorizedEntity { get; set; }
public string connectionType { get; set; }
public string platform { get; set; }
public Dictionary<string, topicItems> rel { get; set; }
}
public class topicItems : Dictionary<string, topicData> { }
public class topicData
{
public DateTime addDate { get; set; }
}
But if they add another node to rel in addition to 'topics' this most likely will crash.
Just doesn't seem to be a very good way to structure the data and the deserialised class isn't very user friendly either..
Anyway it works (for now)
I would like to access the email of this class into another class that I am already instantiated on my render page.
Any help very much appreciated.
public class Access
{
public string Email { get; set; } //I want this value...
}
public class Types
{
public string Id { get; set; }
public string Sum { get; set; }
public string Addition { get; set; }
public string Email { get; set; } // ...to be this value inside my class TYPES
}
Well I think you can simply use a copy constructor for the Types class with the Access instance as parameter from which you want to copy the email value when creating a new Types instance.
From what you said it seems you don't care if its a particular instance (as your post is a bit confusing between class and instance)
public class Types
{
public string Id { get; set; }
public string Sum { get; set; }
public string Addition { get; set; }
public string Email { get; set; }
public Types(Access access)
{
Email = access.Email
}
}
I've been tasked with adding a feature to an app written by someone who has since left my company. This is implemented with C#/Knockout/Breeze/Durandal. I'm wondering if anyone can answer some of the questions I have regarding the inner workings of knockout and C# interactions:
1) Why is knockout converting an existing ICollection to an observableArray properly, but not a new one that I've added to the model?
2) What is the mechanism that converts the Model to obs/obsArrays? I'm guessing it's got something to do with how the data is pulled back but I'm not sure.
I do not see any js code that would initialize the existing Collection as an observableArray, there's no
vm.existingCollection = ko.observableArray();
He just uses it in is source code as
vm.existingCollection.push(stuff);
Edit to include some code for reference
Here's the model code I'm working with:
public class SimCompare : Report
{
public string LeftGraphName { get; set; }
public int? LeftConsolidationId { get; set; }
public int LeftAsOfDateOffset { get; set; }
public int LeftSimCompareSimulationTypeId { get; set; }
public virtual Consolidation LeftConsolidation { get; set; }
public virtual ICollection<SimCompareSimulationType> LeftSimCompareSimulationTypes { get; set; }
public string RightGraphName { get; set; }
public int? RightConsolidationId { get; set; }
public int RightAsOfDateOffset { get; set; }
public int RightSimCompareSimulationTypeId { get; set; }
public virtual Consolidation RightConsolidation { get; set; }
public virtual ICollection<SimCompareSimulationType> RightSimCompareSimulationTypes { get; set; }
public bool IsQuarterly { get; set; }
public virtual ICollection<SimCompareScenarioType> SimCompareScenarioTypes { get; set; }
}
Then there's a simcompareconfig.js file that starts off like this:
define([
'services/globalcontext',
'viewmodels/simulationselectconfig',
'durandal/app'
],
function (globalContext, simulationselectconfig, app) {
var simCompareVm = function (simCompare) {
var self = this;
this.simCompare = simCompare;
...
There is an existing function to add a scenarioType with a line that looks like this:
simCompare().simCompareScenarioTypes().push(newSimCompareScenarioType);
But that simCompareScenarioTypes() isn't defined anywhere in terms of JS (that I can see). However there is a similar definition in the C# model. This is what's leading me to believe that the model is somehow getting mapped behind the scenes.
Any help is greeeeatly appreciated.
Let's say I have a DbContext sub class that looks like
public partial class DBEntities : DbContext
{ ...//More to come }
and in this class I have 2 DbSet instances:
public DbSet<Config> Config { get; set; }
public DbSet<Parameters> Parameters { get; set; }
that look like:
public partial class Config
{
public string Name { get; set; }
public string Value { get; set; }
public string Override { get; set; }
public string Description { get; set; }
}
AND
public partial class Parameters
{
public string Id { get; set; }
public System.DateTime UpdateTime { get; set; }
public float AzValue_rad { get; set; }
public float E_rad { get; set; }
public float N_rad { get; set; }
}
Now let's say I want to have a configuration xml that will encode saved queries.
How can I do that and keep my type strength?
e.g.
How can I encode the query
using (var db = new DBEntities())
{
var query = from floatTag in db.Config
select floatTag;
where floatTag.Name = "SOME_VALUE"
}
EDIT:
I am developing an application that allows the user to define triggers, for example, when a certain field in the DB equals a certain value.
I want to save those triggers, so they would allow me to remember them the next time my app is loaded. Meaning I want to save the database, table and value the trigger deifnes (i.e DBEntities,Config,"SOME_VALUE")
Then, I want to de-serialize those triggers, and then from code, decode the correct Db context and entity so I could re-execute that query.
We currently have several underlying database tables such as Events, Shop Products, Content Pages etc. each with have shared properties such as having a Name, a details page on the front end of the site, a thumbnail url, an active flag etc.
I'm trying to figure out the most efficient way of creating a class of shared properties that can be used to pass around these objects generically. An example might be the search results page. The search can be done against the name of the collection of data which is actually across multiple tables originally.
I am struggling using inheritance due to all these classes originating from LINQ classes and I don't want to start editing the datacontext designer to suit my needs.
Currently each partial class on my LINQ classes contains a SharedObject method:
public partial class Event
{
public SharedObject SharedObject
{
get
{
return new SharedObject(this);
}
}
...
This is repeated for Events, Shop Products etc. The Shared Object class contains the following:
public class SharedObject
{
public string Reference { get; set; }
public string Name { get; set; }
public string ImageURL { get; set; }
public bool IsVisible { get; set; }
public bool IsAdminVisible { get; set; }
public string FrontEndDetailsURL { get; set; }
public string AdminDetailsURL { get; set; }
public object OriginalObject { get; set; }
public string ObjectDescription { get; set; }
public SharedObject(object originalObject)
{
if (originalObject.GetType() == typeof(Event))
{
Event eventx = (Event)originalObject;
Reference = eventx.Reference;
Name = eventx.Name;
ImageURL = eventx.ImageURL;
IsVisible = eventx.IsActive && !Event.IsArchived;
IsAdminVisible = !eventx.IsArchived;
FrontEndDetailsURL = eventx.DetailsURL;
AdminDetailsURL = eventx.AdminDetailsURL;
OriginalObject = originalObject;
ObjectDescription = "Event";
}
....
Does this sound like a suitable solution?
Consider using an interface. This is much more flexible.
public interface ISharedObject
{
string Reference { get; set; }
string Name { get; set; }
string ImageURL { get; set; }
bool IsVisible { get; set; }
bool IsAdminVisible { get; set; }
string FrontEndDetailsURL { get; set; }
string AdminDetailsURL { get; set; }
object OriginalObject { get; set; }
string ObjectDescription { get; set; }
}
public partial class Event : ISharedObject
{}
I think the most efficient way to pass around your shared objects is to use the "Chain of responsibility pattern" Chain of responsibility
For the inheritance LINQ you think of the use of IQueryable <T>. I hope that it can help you