JSON is a text format for representing structured data that is very convenient in IoT applications.
We have learned how to make HTTP requests (Client) and respond to them (Server). But there is an awkward problem we’ve been avoiding: Data format.
In the previous examples, we built responses by concatenating strings:
string json = "{ \"value\": " + temperature + " }";
This is an abomination. It is error-prone (did you miss a quote?), hard to maintain, and horrible to read. Furthermore, when we receive complex data from an API, “parsing” it manually by looking for commas and brackets is a nightmare.
The solution is Serialization.
- Serialize: Convert a live C# Object (in RAM) into a JSON text string.
- Deserialize: Convert a JSON text string into a ready-to-use C# Object.
In this post, we will learn how to use the nanoFramework.Json library to automate both conversions.
For this tutorial, you need to install the NuGet package: nanoFramework.Json
The nanoFramework.Json Library
In the “big” .NET (desktop/web), we are used to using System.Text.Json or the classic Newtonsoft.Json. However, those libraries are huge and consume a lot of memory, something we don’t have to spare in a microcontroller.
nanoFramework.Json offers an API adapted to the memory constraints of a microcontroller.
Deserialize text into an object
Imagine we make a GET request to a weather API and it returns this:
{
"city": "Madrid",
"temperature": 24.5,
"humidity": 60,
"active": true
}To work with this in C#, the first thing is to create a Class (or struct) that represents that structure.
public class WeatherData
{
public string City { get; set; }
public double Temperature { get; set; }
public int Humidity { get; set; }
public bool Active { get; set; }
}Now, converting the text into an object is a single line of code:
using System.Diagnostics;
using nanoFramework.Json; // <--- Important
// ... Let's assume 'jsonReceived' is the string we got from the HttpClient
string jsonReceived = "{ \"City\": \"Madrid\", \"Temperature\": 24.5, \"Humidity\": 60, \"Active\": true }";
// Deserialize
WeatherData myObject = (WeatherData)JsonConvert.DeserializeObject(jsonReceived, typeof(WeatherData));
// Now we can use the properties with IntelliSense!
Debug.WriteLine($"In {myObject.City} it is {myObject.Temperature} degrees.");
if (myObject.Active)
{
// Complex logic...
}Much better. No more searching for substrings or splitting by commas.
Serialize an object as text
The reverse case is just as important. We want to send our sensor telemetry to the cloud.
Instead of creating the string manually, we create an instance of our class and let the library do the work.
public class Telemetry
{
public string DeviceId { get; set; }
public double Voltage { get; set; }
public long Timestamp { get; set; }
}
// ...
// 1. Create the object and populate it
Telemetry data = new Telemetry()
{
DeviceId = "ESP32_01",
Voltage = 3.35,
Timestamp = DateTime.UtcNow.Ticks
};
// 2. Serialize
string jsonToSend = JsonConvert.SerializeObject(data);
Debug.WriteLine(jsonToSend);
// Output: {"DeviceId":"ESP32_01","Voltage":3.35,"Timestamp":638337216000000000}Now you can put that jsonToSend directly into the StringContent of your POST request (as we saw in the previous lesson).
Working with arrays
What if the API returns a list of things?
[{"id":1}, {"id":2}, {"id":3}]
nanoFramework handles arrays perfectly.
string jsonList = "[{ \"DeviceId\": \"A1\", \"Voltage\": 3.0 }, { \"DeviceId\": \"B2\", \"Voltage\": 4.1 }]";
// Deserialize to an Array of objects
Telemetry[] sensorList = (Telemetry[])JsonConvert.DeserializeObject(jsonList, typeof(Telemetry[]));
foreach(var sensor in sensorList)
{
Debug.WriteLine($"Sensor {sensor.DeviceId} has {sensor.Voltage}V");
}Tips and limitations
Although the library is powerful, remember we are on a microcontroller:
- Public Properties: The serializer only sees
publicproperties. If they are private, it will ignore them. - Dates (
DateTime): JSON does not define a date type. Agree on a format with the API (e.g., ISO 8601 or a timestamp) and, if the serializer doesn’t interpret it, receive it as astringto convert later. - Memory: Serializing very large objects (20KB strings) can cause the ESP32 to run out of RAM and throw an exception. Try to keep JSON messages small and concise.