According to the spec, OData v4 services should return int64 and decimal types in string format when IEEE754Compatible=true
is included in the in the Content-Type
request headerAccept
request header or the $format query string parameter.
Here are the relevant sections:
3 Requesting the JSON Format
The OData JSON format can be requested using the $format query option in the request URL with the media type application/json, optionally followed by format parameters, or the case-insensitive abbreviation json which MUST NOT be followed by format parameters.Alternatively, this format can be requested using the Accept header with the media type application/json, optionally followed by format parameters.
If specified, $format overrides any value specified in the Accept header.
Possible format parameters are:
- ExponentialDecimals
- IEEE754Compatible
- metadata (odata.metadata)
- streaming (odata.streaming)
3.2 Controlling the Representation of Numbers
The IEEE754Compatible=true format parameter indicates that the service MUST serialize Edm.Int64 and Edm.Decimal numbers (including the count, if requested) as strings. This is in conformance with [RFC7493].If not specified, or specified as IEEE754Compatible=false, all numbers MUST be serialized as JSON numbers.
This enables support for JavaScript numbers that are defined to be 64-bit binary format IEEE 754 values [ECMAScript] (see section 4.3.1.9) resulting in integers losing precision past 15 digits, and decimals losing precision due to the conversion from base 10 to base 2.
This doesn't happen for me when using ASP.Net Core OData 8 (even though there are discussions on Github c.2016 where it appears to have been implemented in OData.net: e.g., here).
As a minimal reproducible example:
TestLong
with type long
to the Customer
modellong.MaxValue
Yet, I receive number formatted values in the JSON, instead of string.
UPDATE
Following the comment from @Jonathan Alfaro, I investigated to try and find where the IsIeee754Compatible parameter is actually set. (No luck so far).
I created a new jsonwriter factory as described in this dev blog:
using Microsoft.OData.Json;
using System.Text;
namespace TestIEEE754Compatibility.Models
{
public class CustomStreamBasedJsonWriterFactory : IStreamBasedJsonWriterFactory
{
public IJsonWriterAsync CreateAsynchronousJsonWriter(Stream stream, bool isIeee754Compatible, Encoding encoding)
{
return DefaultStreamBasedJsonWriterFactory.Default.CreateAsynchronousJsonWriter(stream, isIeee754Compatible, encoding);
}
public IJsonWriter CreateJsonWriter(Stream stream, bool isIeee754Compatible, Encoding encoding)
{
return DefaultStreamBasedJsonWriterFactory.Default.CreateJsonWriter(stream, isIeee754Compatible, encoding);
}
}
}
Which I injected as below:
var edmModel = modelBuilder.GetEdmModel();
builder.Services.AddControllers().AddOData(
options => {
options.Select().Filter().OrderBy().Expand().Count().SetMaxTop(null).AddRouteComponents(
"odata",
edmModel,
services =>
{
services.AddSingleton<IStreamBasedJsonWriterFactory>(o => new CustomStreamBasedJsonWriterFactory());
});
});
And the isIeee754Compatible
parameter is being passed false
even though the IEEE754Compatible=true
parameter is present (see screenshot below). So, clearly it doesn't seem to be set automatically. But I can't for the life of me find out where it can be set manually.
This wasn't fully implemented in ASP.NET Core OData 8. I raised an issue on GitHub and it was quickly resolved.
The GitHub issue Feature is merged into main
I've confirmed using nightly build 8.2.3-Nightly202312181316
that it is resolved. You just need to add IEEE754Compatible=true
to either the Accept
request header, or the $format
query. There are no other changes you need to make in your code, OData handles it internally.