Description
We have a model class that represents our JSON data model. For some fields we have dynamic data types. Value can be string, integer, double, boolean, etc.
So for this we use a model class that defines the value as JsonElement so we can access the JsonValueKind and parse it depending on our needs and what it actually represents.
public sealed record DocumentValue(JsonElement Value, bool ValueFound, double Confidence, RelationalOperator? Operator, string Unit);
We parse our incoming JSON like this JsonSerializer.Deserialize<TMessage>(data, KafkaAdapterFactory.SerializationOptions) where in that case TMessage is our model which contains a list of DocumentValue at certain points in the structure.
Here's an example of the JSON (just a fragment) what we parse:
"fields": [
{
"name": "Count",
"value": {
"value": 5,
"valueFound": true,
"confidence": 0,
"operator": null,
"unit": null
}
}, // ...
]
As you see, it's clearly no floating point number (and will never be in that case). When I now set a breakpoint on this line:
var actualCount = count.Value.Value.GetInt32();
I get an FormatException. On the breakpoint I can verify that value kind is JsonValueKind.Number. So it should certainly be able to use GetInt32() without issues.
The exception looks like this:
System.FormatException: One of the identified items was in an invalid format.
at void System.Text.Json.ThrowHelper.ThrowFormatException()
at int System.Text.Json.JsonElement.GetInt32()
Checking the JsonElement with the debugger it says: Number: 5.0. When using GetDouble() and converting this into an int it works fine.
PS: To clarify, the setting used for deserialization are these:
internal static JsonSerializerOptions SerializationOptions = new()
{
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) },
PropertyNameCaseInsensitive = true,
WriteIndented = false
};
EDIT: More detailed information as I've stepped into it:
if (Utf8Parser.TryParse(segment, out int tmp, out int consumed) &&
consumed == segment.Length)
Here segment is a slice of 3 bytes, the call returns false, tmp: 5, consumed: 1, comsumed (1) == segment.Length (3). These are the values of it. So seems like while it parses it correctly, the check still fails due to the segment length mismatching the consumed parameter.
Reproduction Steps
Use a model with JsonElement as property type. Parse a JSON document where this value is represented as non-floating number i.e. "5". Deserialize using JsonSerializer.Deserialize<TMessage>() and evaluate the actual value. It should be of kind Number and GetInt32() should work on this element.
Expected behavior
JsonElement.GetInt32() should return a valid value. Even if it's internally represented as floating-point number, it should be able to convert it.
Actual behavior
A FormatException is being thrown.
Regression?
Can't tell, newly implemented feature that wasn't there before we upgraded to .NET 8.
Known Workarounds
Retrieving the value using JsonElement.GetDouble() and converting it works but is an unncessary extra step that shouldn't be necessary.
Configuration
.NET 8 (latest)
Windows 11, Linux (not sure which one exactly)
x64
Likely unrelated to configuration
Other information
No response
Description
We have a model class that represents our JSON data model. For some fields we have dynamic data types. Value can be string, integer, double, boolean, etc.
So for this we use a model class that defines the value as
JsonElementso we can access theJsonValueKindand parse it depending on our needs and what it actually represents.public sealed record DocumentValue(JsonElement Value, bool ValueFound, double Confidence, RelationalOperator? Operator, string Unit);We parse our incoming JSON like this
JsonSerializer.Deserialize<TMessage>(data, KafkaAdapterFactory.SerializationOptions)where in that caseTMessageis our model which contains a list ofDocumentValueat certain points in the structure.Here's an example of the JSON (just a fragment) what we parse:
As you see, it's clearly no floating point number (and will never be in that case). When I now set a breakpoint on this line:
var actualCount = count.Value.Value.GetInt32();I get an
FormatException. On the breakpoint I can verify that value kind isJsonValueKind.Number. So it should certainly be able to useGetInt32()without issues.The exception looks like this:
Checking the
JsonElementwith the debugger it says:Number: 5.0. When usingGetDouble()and converting this into an int it works fine.PS: To clarify, the setting used for deserialization are these:
EDIT: More detailed information as I've stepped into it:
Here segment is a slice of 3 bytes, the call returns
false,tmp: 5,consumed: 1,comsumed (1) == segment.Length (3). These are the values of it. So seems like while it parses it correctly, the check still fails due to the segment length mismatching the consumed parameter.Reproduction Steps
Use a model with
JsonElementas property type. Parse a JSON document where this value is represented as non-floating number i.e. "5". Deserialize usingJsonSerializer.Deserialize<TMessage>()and evaluate the actual value. It should be of kindNumberandGetInt32()should work on this element.Expected behavior
JsonElement.GetInt32()should return a valid value. Even if it's internally represented as floating-point number, it should be able to convert it.Actual behavior
A
FormatExceptionis being thrown.Regression?
Can't tell, newly implemented feature that wasn't there before we upgraded to .NET 8.
Known Workarounds
Retrieving the value using
JsonElement.GetDouble()and converting it works but is an unncessary extra step that shouldn't be necessary.Configuration
.NET 8 (latest)
Windows 11, Linux (not sure which one exactly)
x64
Likely unrelated to configuration
Other information
No response