Decoding and parsing JSON using NewtonSoft

So you are working with JSON, you get a JSON message and you want to retrieve some values from it. How?

Let’s suppose you get something like (this is an example take from here):

"{\"port\":1,\"counter\":39,\"payload_raw\":\"KAA=\",\"payload_fields\":{\"errorCode\":0,\"numberOfCycles\":40},\"metadata\":{\"time\":\"2017-01-15T22:55:04.204820257Z\",\"frequency\":868.1,\"modulation\":\"LORA\",\"data_rate\":\"SF7BW125\",\"coding_rate\":\"4/5\",\"gateways\":[{\"gtw_id\":\"eui-b827ebffffc19ca8\",\"timestamp\":4113032806,\"time\":\"1754-08-30T22:43:41.128654848Z\",\"channel\":0,\"rssi\":-90,\"snr\":8,\"latitude\":51.46018,\"longitude\":5.61902,\"altitude\":10}]}}"

The backslashes, the escape characters, are only for C# to know which double quotes are part of the message. It’s not necessary but you can remove them and then you have:

{"port":1,"counter":39,"payload_raw":"KAA=","payload_fields":{"errorCode":0,"numberOfCycles":40},"metadata":{"time":"2017-01-15T22:55:04.204820257Z","frequency":868.1,"modulation":"LORA","data_rate":"SF7BW125","coding_rate":"4/5","gateways":[{"gtw_id":"eui-b827ebffffc19ca8","timestamp":4113032806,"time":"1754-08-30T22:43:41.128654848Z","channel":0,"rssi":-90,"snr":8,"latitude":51.46018,"longitude":5.61902,"altitude":10}]}}

Well, it’s always good to check the message is proper JSON. You can check it with a ‘tool’ like http://jsonlint.com/

jsonlint

This is a valid message, if we format the structure, it looks a lot better:

{
"port": 1,
"counter": 504,
"payload_raw": "+QA=",
"payload_fields": {
"errorCode": 0,
"numberOfCycles": 249
},
"metadata": {
"time": "2017-01-10T23:31:06.087189682Z",
"frequency": 868.1,
"modulation": "LORA",
"data_rate": "SF7BW125",
"coding_rate": "4/5",
"gateways": [{
"gtw_id": "eui-b827ebffffc19ca8",
"gtw_trusted": true,
"timestamp": 3771642998,
"time": "1754-08-30T22:43:41.128654848Z",
"channel": 0,
"rssi": -80,
"snr": 9,
"latitude": 51.46018,
"longitude": 5.61902,
"altitude": 10}]
}
}

There are several ways to get values from this JSON message.

Here are three ways you can access your data using the NewtonSoft JSON NuGet package.

Deserialize the JSON message to a C# Class structure

Because the structure of this message is known and it is unlikely it will change in the near future. We can deserialize it to pre-built  C# classes. All we have to do is write classes which are referencing each other, just like the message. The properties need to match with their name and value type exactly with the JSON structure:

public class Message
{
    public int port { get; set; }
    public int counter { get; set; }
    public string payload_raw { get; set; }
    public payload_fields payload_fields { get; set; }
    public metadata metadata { get; set; }
}
 
public class payload_fields
{
    public int errorCode { get; set; }
    public int numberOfCycles { get; set; }
}
 
public class metadata
{
    public string time { get; set; }
    public decimal frequency { get; set; }
    public string modulation { get; set; }
    public string data_rate { get; set; }
    public string coding_rate { get; set; }
    public List<gateway> gateways { get; set; }
}
 
public class gateway
{
    public string gtw_id { get; set; }
    public bool gtw_trusted { get; set; }
    public int timestamp { get; set; }
    public string time { get; set; }
    public int channel { get; set; }
    public int rssi { get; set; }
    public int snr { get; set; }
    public decimal latitude { get; set; }
    public decimal longitude { get; set; }
    public int altitude { get; set; }
}

After that, you can deserialize the message into typed classes:

var jsonMessage = "{\"port\":1,\"counter\":592,\"payload_raw\":\"UQA = \",\"payload_fields\":{\"errorCode\":0,\"numberOfCycles\":81},\"metadata\":{\"time\":\"2017 - 01 - 10T23: 48:51.284419006Z\",\"frequency\":868.3,\"modulation\":\"LORA\",\"data_rate\":\"SF7BW125\",\"coding_rate\":\"4 / 5\",\"gateways\":[{\"gtw_id\":\"eui - b827ebffffc19ca8\",\"gtw_trusted\":true,\"timestamp\":541872895,\"time\":\"1754 - 08 - 30T22: 43:41.128654848Z\",\"channel\":1,\"rssi\":-77,\"snr\":9,\"latitude\":51.46018,\"longitude\":5.61902,\"altitude\":10}]}}";
 
var message = JsonConvert.DeserializeObject<Message>(jsonMessage);
 
Console.WriteLine(message.port);
Console.WriteLine(message.counter);
Console.WriteLine(message.payload_raw);
Console.WriteLine(message.payload_raw.errorcode);

This is the way I prefer. It’s clean and it’s typed, no room for errors. But it’s also very strict.

The “I am too lazy to type the complete structure, I just take what I need” way

Ok, if the JSON structure is very large or you only want to write the lease amount of code, you can fall back to a Dynamic. A Dynamic value can hold typed properties but only after compilation. At design time it is not yet defined. This is perfect for our lazy approach:

dynamic message = JsonConvert.DeserializeObject(jsonMessage);

int counter = message.counter;

decimal frequency = message.metadata.frequency; // 868.3

var jsonValue = message.metadata.frequency; // {868.3}

var typeAsText = message.GetType().ToString(); // Newtonsoft.Json.Linq.JValue

int length = bb.metadata.gateways.Count;

int channel = message.metadata.gateways[0].channel;

As you can see, we can read the correct values, but only if we already know the name, type and structure of each property. Using ‘var’ will not help you. But looping through array’s is not a problem at all.

Warning: Better check for NULL values (in case of mistyped or missing JSON elements (var jsonValue = message.metadata?.frequency;))

The “I do not know what structure will arrive!” way

Finally, it’s possible (although most possibly a code smell) that the structure of the incoming message is just unknown. So you want so build extensive logic to check if certain values exist and act on them.

Even then, we are able to support this:

var jsonObject = JObject.Parse(jsonMessage);

var frequency = jsonObject.SelectToken("metadata.frequency").ToString();

var rssi = jsonObject.SelectToken("metadata.gateways[0].rssi").ToString();

foreach (var p in jsonObject) // properties
{
    Console.WriteLine(p.Key);  // eg. port
    Console.WriteLine(p.Value.Type); // eg. integer
}

var port = Convert.ToInt32(jsonObject.SelectToken("port").ToString());

This gives us the most flexibility, we can probe for certain properties and check out their type before we cast their values.

Warning: be aware of missing or mistyped element names. If ‘metadata.frequency’ does not exist, this code will crash (null referenced by ‘ToString()’)

Conclusion

JSON is not that hard to decode and parse. You only need the right tooling. NewtonSoft provides that tooling. And because it’s a NuGet package, it available everywhere, including Azure Functions.

Advertenties