I currently have an API request that I make with Avalonia C# and I manage to return the entire JSON as a result but cannot retrieve each element in a foreach. Here is the current code:
// Get servers infos
private async Task GetServersInfos()
{
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("GET"), "http://example.com/api"))
{
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string jsonObject = JsonSerializer.Serialize(response.Content);
var results = await response.Content.ReadAsStringAsync();
foreach (var result in results)
{
TestText.Text = result["name"];
}
}
}
}
}
And the JSON result:
{
"1": {
"name": "Example 1",
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
},
"2": {
"name": "Example 2",
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
}
}
But I have the following error: Unable to apply indexing using [] to an expression of type 'char'.
You must use JsonSerializer.DeserializeAsync
to convert the known JSON to a C# data type or use JsonDocument
to convert the unknown JSON response to a read-only DOM model and traverse its element nodes.
To improve the performance, you should pass a Stream
to JsonSerializer.SeserializeAsync
as this eliminates one complete enumeration of the response message. To issue a HTTP GET request, simply use one of the Get...
methods provided by the HttpClient
API.
In our case we pick the HttpClient.GetStreamAsync
method.
The response message is a single JSON object (and not an array) that is constructed of key-value pairs. That makes a list of key-value pairs. Which is exactly how the Dictionary
structure is built.
That's a crucial finding as this means we can deserialize every well-formed JSON object into a Dictionary<string, object>
.
The only special case is if the JSON contains an array. In this case we must tell the JsonSerializer
how to convert it, which means we have to create a custom JsonConverter
. In any other case we can always use Dictionary<string, string>
to deserialize or serialize a JSON object.
However, to handle complex unknown JSON content the recommended way would be to use JsonDocument
.
Deserialize a known simple JSON that contains no arrays in a very general manner using JsonSerializer
(doesn't require a custom data structure).
In your example case we expect a list of nested JSON objects, which can be represented a nested Dictionary
in the form of Dictionary<string, Dictionary<string, string>>
:
// Get server JSON response as Stream
private async Task GetServersInfos()
{
using var httpClient = new HttpClient()
string url = "http://example.com/api";
await using Stream responseStream = await httpClient.GetStreamAsync(url);;
var serializerOptions = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true };
Dictionary<string, Dictionary<string, string>>? responseData = await JsonSerializer.DeserializeAsync<Dictionary<string, Dictionary<string, string>>>(responseStream, serializerOptions);
/* * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Example how to handle the response object *
* *
* * * * * * * * * * * * * * * * * * * * * * * * */
Dictionary<string, string> firstDataEntry = responseData["1"];
string nameValue = firstDataEntry["name"]; // "Example 1"
string descriptionValue = firstDataEntry["description"]; // "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
Dictionary<string, string> secondDataEntry = responseData["2"];
nameValue = secondDataEntry["name"]; // "Example 2"
descriptionValue = secondDataEntry["Description"]; // "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
// Or enumerate the response JSON:
foreach (KeyValuePair<string, Dictionary<string, string>> entry in responseData)
{
string jsonObjectPropertyName = entry.Key; // "1"
Dictionary<string, string> nestedJsonObject = entry.Value;
foreach (KeyValuePair<string, string> nestedObjectEntry in nestedJsonObject)
{
jsonObjectPropertyName = nestedObjectEntry.Key; // "name" or "description"
string jsonObjectPropertyValue = nestedObjectEntry.Value; // "Example 1" or "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
// For example, only get the "description" value
if (nestedObjectEntry.Key.Equals("Description", StringComparison.OrdinalIgnoreCase))
{
string descriptionText = nestedObjectEntry.Value; // "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
}
}
}
}
Deserialize a known JSON that contains no arrays in a very specialized manner using JsonSerializer
.
In your example case we expect a list of nested JSON objects, which enables us to create a C# type for improved convenience in terms of data handling:
DataSet.cs ``c# public class DataSet { public string Name { get; } public string Description { get; } }
```c#
// Get server JSON response as Stream
private async Task GetServersInfos()
{
using var httpClient = new HttpClient()
string url = "http://example.com/api";
await using Stream responseStream = await httpClient.GetStreamAsync(url);
var serializerOptions = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true };
Dictionary<int, DataSet>? responseData = await JsonSerializer.DeserializeAsync<Dictionary<int, DataSet>>(responseStream, serializerOptions);
/* * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Example how to handle the response object *
* *
* * * * * * * * * * * * * * * * * * * * * * * * */
DataSet firstDataEntry = responseData[1];
string nameValue = firstDataEntry.Name; // "Example 1"
string descriptionValue = firstDataEntry.Description; // "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
DataSet secondDataEntry = responseData[2];
nameValue = secondDataEntry.Name; // "Example 2"
descriptionValue = secondDataEntry.Description; // "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
}
Deserialize an unknown JSON using JsonDocument
.
We can traverse and inspect every JSON using the JSON's read-only DOM.
If we need to manipulate the JSON document we would use JsonNode
instead.
However, JsonDocument
seems to be sufficient and it is faster than JsonNode
:
// Get server JSON response as Stream
private async Task GetServersInfos()
{
using var httpClient = new HttpClient()
string url = "http://example.com/api";
await using Stream responseStream = await httpClient.GetStreamAsync(url);
using JsonDocument responseJson = await JsonDocument.ParseAsync(responseStream);
/* * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Example how to handle the response object *
* *
* * * * * * * * * * * * * * * * * * * * * * * * */
// Because the response is an JSON object and not a JSON array
// we have to call EnumerateObject instead of EnumerateArray
if (responseJson.RootElement.ValueKind is JsonValueKind.Object)
{
foreach (JsonProperty jsonObject in responseJson.RootElement.EnumerateObject())
{
string propertyName = jsonObject.Name; // "1" and "2"
JsonElement nestedObject = jsonObject.Value;
// Enumerate the JSON object, which has two properties "name" and "description",
// property by property (makes for two iterations)
if (nestedObject.ValueKind is JsonValueKind.Object)
{
foreach (JsonProperty dataEntry in nestedObject.EnumerateObject())
{
string entryName = dataEntry.Name; // "name" and "description"
string? entryValue = dataEntry.Value.GetString(); // "Example 1" and "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
// For example, only get the "description" propertyValue
if (dataEntry.Name.Equals("Description", StringComparison.OrdinalIgnoreCase))
{
string descriptionText = dataEntry.Value.GetString();
}
}
}
}
}
}