I am trying to deserialize the following JSON (an array with a single entry):
[
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"birth_year": "19BBY",
"gender": "male"
}
]
Into this record:
internal record class Character
{
public string Name { get; init; } = "";
[property: JsonPropertyName("birth_year")]
public double Birth { get; init; }
public int Height { get; init; }
[property: JsonPropertyName("mass")]
public int Weight { get; init; }
public GenderEnum Gender { get; init; }
}
With the following JsonSerializerOptions
setup:
var options = new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true,
NumberHandling = JsonNumberHandling.AllowReadingFromString,
Converters =
{
new GenderEnumConverter(),
new BirthYearConverter(),
new MeasurementsConverter()
}
};
The two top converters work well. It's when I add the MeasurementsConverter
that I get an exception:
internal class MeasurementsConverter : JsonConverter<int>
{
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.TryGetInt32(out int result) ? result : -1;
}
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
The exception is thrown on the Read
method:
System.InvalidOperationException: 'Cannot get the value of a token type 'String' as a number.'
When I deserialize with the following two expressions:
string result = await File.ReadAllTextAsync("people.json");
List<Character> people = JsonSerializer.Deserialize<List<Character>>(result, options);
Can you help me understand why the MeasurementsConverter : JsonConverter<int>
is ignoring the NumberHandling = JsonNumberHandling.AllowReadingFromString
option?
Note: This json is a sample from swapi.dev/api/people. If you know the API, at least one entry in the resulting array will have "unknown"
as a value for the weight attribute. Hence, this converter.
From Jon's comment above, I was able to avoid triggering the exception by making explicit use of the Utf8JsonReader.TokenType
property of the reader object.
The Read() override now looks like this:
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if(reader.TokenType == JsonTokenType.String && options.NumberHandling == JsonNumberHandling.AllowReadingFromString)
{
return int.TryParse(reader.GetString(), out int result) ? result : -1;
}
return reader.GetInt32();
}