Reputation: 1298
I have the following piece of code which reads incoming message from an event hub and stores it in blob storage.
dynamic msg = JObject.Parse(myEventHubMessage);
WriteToBlob(msg, enqueuedTimeUtc, myEventHubMessage, binder, log);
Here are some examples of the JSON I receive:
{
"deviceId": "ATT",
"product": "testprod",
"data": {
"001": 1,
"002": 3.1,
"003": {
"lat": 0,
"lng": 0
},
"000": -80
},
"ts": "2020-01-27T19:29:34Z"
}
{
"deviceId": "ATT",
"product": "testprod",
"data_in": {
"ts": "2020-01-27T19:29:34Z",
"001": 1,
"002": 3.1,
"003": {
"lat": 0,
"lng": 0
},
"000": -80
}
}
Now, instead of a 'data' node in the JSON, sometimes the device sends the node with the name 'data_in'. And the ts field can sometimes be inside or outside the data or data_in node, and maybe named timestamp. How can I efficiently determine if a node exists or not?
I was thinking of doing something like this:
if (msg.data_in.ts != null)
{
}
And I would do the same for all conditions. Is there a way to better achieve this? Also, the if condition fails if I check msg.data_in.ts if data_in node doesn't exist.
Upvotes: 1
Views: 299
Reputation: 117344
Your problem is that you have upcast your JObject
to dynamic
. This makes things difficult for the following reasons:
You loose all compile-time checking for code correctness.
You loose convenient access to the methods and properties of JObject
itself (as opposed to the dynamically provided JSON properties).
Since JObject
implements interfaces such as IDictionary<string, JToken>
leaving it as a typed object will make the job of checking for, adding and removing select JSON properties easier.
To see why working with a typed JObject
can be easier, first introduce the following extension methods for convenience:
public static class JsonExtensions
{
public static JProperty Rename(this JProperty old, string newName)
{
if (old == null)
throw new ArgumentNullException();
var value = old.Value;
old.Value = null; // Prevent cloning of the value by nulling out the old property's value.
var @new = new JProperty(newName, value);
old.Replace(@new); // By using Replace we preserve the order of properties in the JObject.
return @new;
}
public static JProperty MoveTo(this JToken token, JObject newParent)
{
if (newParent == null || token == null)
throw new ArgumentNullException();
var toMove = (token as JProperty ?? token.Parent as JProperty);
if (toMove == null)
throw new ArgumentException("Incoming token does not belong to an object.");
if (toMove.Parent == newParent)
return toMove;
toMove.Remove();
newParent.Add(toMove);
return toMove;
}
}
And now you can normalize your messages as follows:
var msg = JObject.Parse(myEventHubMessage);
msg.Property("data_in")?.Rename("data"); // Normalize the name "data_in" to be "data".
msg["data"]?["ts"]?.MoveTo(msg); // Normalize the position of the "ts" property, it should belong to the root object
msg["data"]?["timestamp"]?.MoveTo(msg); // Normalize the position of the "timestamp" property, it should belong to the root object
msg.Property("timestamp")?.Rename("ts"); // And normalize the name of the "timestamp" property, it should be "ts".
Demo fiddle here.
Upvotes: 1