Situation
I am running into an issue with my implementation of .NET Core's System.Text.Json.JsonSerializer. The API which my application utilizes for getting data returns JSON in the following format:
{
"context": "SomeUnusedContextValue",
"value": [
{...}
]
}
I only care for the actual response, so I only need the value item.
I have written below method to get the specific property and deserialize the items into objects.
public static async Task<T?> DeserializeResponse<T>(Stream str, string? property, CancellationToken ct)
{
JsonDocument jsonDocument = await JsonDocument.ParseAsync(str, default, ct).ConfigureAwait(false);
if (property is null) // some calls to the API do return data at root-level
{
return JsonSerializer.Deserialize<T>(jsonDocument.RootElement.GetRawText());
}
if (!jsonDocument.RootElement.TryGetProperty(property, out JsonElement parsed))
throw new InvalidDataException($"The specified lookup property \"{property}\" could not be found.");
return JsonSerializer.Deserialize<T>(!typeof(IEnumerable).IsAssignableFrom(typeof(T))
? parsed.EnumerateArray().FirstOrDefault().GetRawText()
: parsed.GetRawText(), new JsonSerializerOptions
{ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault });
}
Problem
Now for my problem. Sometimes I only need a single object, however even if there is only one result, the API will still return an array. Not a problem since, as can be seen in the bottom return statement, I will just enumerate the array and get the first item (or the default null
). This seems to crash when the returned array is empty, throwing below exception:
System.InvalidOperationException: Operation is not valid due to the current state of the object.
at System.Text.Json.JsonElement.GetRawText()
at BAS.Utilities.Deserializing.ResponseDeserializer.DeserializeResponse[T](Stream str, String property, CancellationToken ct) in C:\dev\bas.api\Modules\BAS.Utilities\Deserializing\ResponseDeserializer.cs:line 40
The object I'm trying to serialize into is as below:
public class JobFunctionCombination
{
/// <summary>
/// Gets or sets the combined identifier of the main function group, the function group and the sub function group.
/// </summary>
/// <example>01_0101_010101</example>
[JsonPropertyName("job_function_combination_id")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Id { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the combined names of the function groups.
/// </summary>
/// <example>Management | Human Resources | Finance controller</example>
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Description { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the identifier of the main function group.
/// </summary>
[JsonPropertyName("job_main_function_group_id")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string MainFunctionGroupId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the identifier of the function group.
/// </summary>
[JsonPropertyName("job_function_group_id")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string FunctionGroupId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the identifier of the sub function group.
/// </summary>
[JsonPropertyName("job_sub_function_group_id")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string SubFunctionGroupId { get; set; } = string.Empty;
}
The types and JsonPropertyName attributes all match to the returned JSON.
Attempted fixes
To try and fix this issue, I have tried some fixes (two of which you can still see in the given code samples).
- Adding the
JsonIgnore
attribute to the properties in the class.- I have tried to set the condition to
WhenWritingDefault
as well asWhenWritingNull
. Neither seem to fix the problem.
- I have tried to set the condition to
- Setting the
DefaultIgnoreCondition
in aJsonSerializerOptions
object passed toJsonSerializer.Deserialze
.- Here I have also tried both
WhenWritingDefault
andWhenWritingNull
, also to no avail.
- Here I have also tried both
- Checking if the array in
JsonElement
, when enumerated, is null or empty using.isNullOrEmpty()
.- This does prevent the exception from occuring, however it does not seem like an actual fix to me. More like a FlexTape solution just cancelling out the exception.
I am not sure what the exact issue is, other than the clear fact that JsonSerializer
clearly has an issue with null objects. What could I do to fix this?