Serialize and deserialize a multidimensional array to JSON in C#
By FoxLearn 12/25/2024 4:46:11 AM 40
When attempting to serialize or deserialize a two-dimensional array, for example, it throws a System.NotSupportedException
with the message: The type 'System.Int[,] is not supported. Fortunately, there are ways to work around this limitation.
In this article, we will explore three potential solutions to handle multidimensional arrays with System.Text.Json
:
- Use Newtonsoft.Json, which supports multidimensional arrays.
- Use a jagged array (e.g.,
int[][]
), whichSystem.Text.Json
can handle. - Write a custom JsonConverter to handle multidimensional arrays manually
1. Create a Custom JsonConverter Class
The first step is to create a class that implements JsonConverter<int[,]>
, which can handle the serialization and deserialization of two-dimensional integer arrays.
using System.Text.Json.Serialization; using System.Text.Json; public class TwoDimensionalIntArrayJsonConverter : JsonConverter<int[,]> { public override int[,]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { throw new NotImplementedException(); } public override void Write(Utf8JsonWriter writer, int[,] value, JsonSerializerOptions options) { throw new NotImplementedException(); } }
2. Serialize a Multidimensional Array in the Write()
Method
To serialize the multidimensional array in the Write()
method, we need to convert the two-dimensional array into a JSON array of arrays. Here's the implementation for serializing a two-dimensional array of integers:
- Start the array with
writer.WriteStartArray()
. - Use
writer.WriteNumberValue()
to write each integer value to the array. - Close the array with
writer.WriteEndArray()
.
public override void Write(Utf8JsonWriter writer, int[,] value, JsonSerializerOptions options) { writer.WriteStartArray(); // Start the outer array for (int i = 0; i < value.GetLength(0); i++) { writer.WriteStartArray(); // Start the inner array (row) for (int j = 0; j < value.GetLength(1); j++) { writer.WriteNumberValue(value[i, j]); // Write each number } writer.WriteEndArray(); // End the inner array (row) } writer.WriteEndArray(); // End the outer array }
3. Deserialize to a Multidimensional Array in the Read()
Method
When we serialize the two-dimensional array, it is written as a JSON array of arrays, like this:
[[1, 2], [3, 4]]
To deserialize this JSON back into a two-dimensional array, we will perform two main steps:
- Initialize the two-dimensional array: We first need to determine the dimensions of the array. Since
Utf8JsonReader
is forward-only, we useJsonDocument
to parse the JSON and find out the sizes of the outer and inner arrays. - Loop through the nested arrays: We will iterate over the JSON array, reading the values and placing them into the two-dimensional array.
public override int[,]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var jsonDoc = JsonDocument.ParseValue(ref reader); // Get the length of the outer array (rows) and the inner array (columns) var rowLength = jsonDoc.RootElement.GetArrayLength(); var columnLength = jsonDoc.RootElement.EnumerateArray().First().GetArrayLength(); int[,] grid = new int[rowLength, columnLength]; int row = 0; foreach (var array in jsonDoc.RootElement.EnumerateArray()) { int column = 0; foreach (var number in array.EnumerateArray()) { grid[row, column] = number.GetInt32(); // Assign values to the grid column++; } row++; } return grid; }
In this implementation:
- We first parse the JSON with
JsonDocument.ParseValue()
to get the size of the array. - We then iterate through the arrays, populating the two-dimensional
int[,]
array.
4. Use the Custom JsonConverter
Once the JsonConverter
is implemented, it can be used with JsonSerializerOptions
. To apply the converter, you need to add it to the Converters
collection of JsonSerializerOptions
:
using System.Text.Json; int[,] grid = new int[,] { { 5, 10, 15 }, { 20, 25, 30 }, { 35, 40, 45 } }; var options = new JsonSerializerOptions(); options.Converters.Add(new TwoDimensionalIntArrayJsonConverter()); var json = JsonSerializer.Serialize(grid, options); Console.WriteLine($"Serialized: {json}"); var gridFromJson = JsonSerializer.Deserialize<int[,]>(json, options); Console.WriteLine("Deserialized:"); Console.WriteLine($"[0,0] = {gridFromJson[0,0]}"); Console.WriteLine($"[0,1] = {gridFromJson[0,1]}"); Console.WriteLine($"[0,2] = {gridFromJson[0,2]}"); Console.WriteLine($"[1,0] = {gridFromJson[1,0]}"); Console.WriteLine($"[1,1] = {gridFromJson[1,1]}"); Console.WriteLine($"[1,2] = {gridFromJson[1,2]}"); Console.WriteLine($"[2,0] = {gridFromJson[2,0]}"); Console.WriteLine($"[2,1] = {gridFromJson[2,1]}"); Console.WriteLine($"[2,2] = {gridFromJson[2,2]}");
The output will show the serialized JSON string and the deserialized values of the 3x3 matrix:
Serialized: [[5,10,15],[20,25,30],[35,40,45]] Deserialized: [0,0] = 5 [0,1] = 10 [0,2] = 15 [1,0] = 20 [1,1] = 25 [1,2] = 30 [2,0] = 35 [2,1] = 40 [2,2] = 45
In this example, we are serializing and deserializing a 3x3 integer array with the custom JsonConverter
we created earlier. The JSON string that represents this array is [[5,10,15],[20,25,30],[35,40,45]]
, and upon deserialization, we are able to retrieve each value from the respective positions in the array.
If you prefer to avoid custom converters, other solutions like using Newtonsoft.Json or jagged arrays (e.g., int[][]
) are also valid options. However, writing a custom JsonConverter
provides a flexible and efficient way to handle this scenario.