Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect Serialization of long (Int64) Values in ASP.NET Core Web API #55571

Closed
1 task done
Nodirbek-Abdulaxadov opened this issue May 7, 2024 · 9 comments
Closed
1 task done
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates

Comments

@Nodirbek-Abdulaxadov
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

When returning a Value object containing a long property from ASP.NET Core Web API controllers using the OkObjectResult, the serialization of big long values like Int64.MaxValue is incorrect. This leads to loss of precision in the returned JSON response.

Expected Behavior

The JSON response should accurately represent the long value assigned to the LongValue property of the Value object.

Steps To Reproduce

  1. Create an ASP.NET Core Web API project.
  2. Define a model with a long property, such as Value in the provided code snippet.
  3. Implement a controller action that returns an instance of this model using Ok() method( OkObjectResult).
  4. Assign a long value Int64.MaxValue to the long property of the model.
  5. Send a request to the API endpoint corresponding to the controller action.
  6. Observe the returned JSON response.

Exceptions (if any)

The JSON response shows a loss of precision in the long value, with the last digits being incorrect, especially when the long value exceeds Int64.MaxValue.

.NET Version

8.0.204

Anything else?

Example code:

using Microsoft.AspNetCore.Mvc;

namespace WebApplication1.Controllers;

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    [HttpGet("test1")]
    public IActionResult GetTest1()
    {
        long max = 9223372036854775807;
        Value value = new()
        {
            LongValue = max,
            LongValueAsString = max.ToString()
        };
        return Ok(value);
        /* Result is:
            {
                "longValue": 9223372036854776000,
                "longValueAsString": "9223372036854775807"
            }
        */
    }

    [HttpGet("test2")]
    public IActionResult GetTest2()
    {
        long max = long.MaxValue;
        Value value = new()
        {
            LongValue = max,
            LongValueAsString = max.ToString()
        };
        return Ok(value);
        /* Result is:
            {
              "longValue": 9223372036854776000,
              "longValueAsString": "9223372036854775807"
            }
        */
    }
}

public class Value
{
    public long LongValue { get; set; }
    public string LongValueAsString { get; set; } = string.Empty;
}
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label May 7, 2024
@martincostello
Copy link
Member

I think this is essentially a duplicate of #16117 (comment).

Try setting JsonSerializationOptions.NumberHandling = JsonNumberHandling.WriteAsString.

@Nodirbek-Abdulaxadov
Copy link
Author

Yes this can be a workaround:

JsonSerializerOptions options = new()
{
    NumberHandling = JsonNumberHandling.Strict,
    WriteIndented = true
};

return Ok(JsonSerializer.Serialize(value, options));

But exactly when will the problem with Ok(object) be solved?

@martincostello
Copy link
Member

AFAIK Ok() uses the globally configured MVC options for JSON serialization, so if you change the defaults to enable formatting long as string for your use case it will be resolved.

services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.NumberHandling = JsonNumberHandling.WriteAsString;
    });

Changing the defaults for everyone would likely be a breaking change.

@Nodirbek-Abdulaxadov
Copy link
Author

But I don't want to use longValue as a string. So I tried to set it up as you said but it didn't work:

builder.Services.AddControllers().AddJsonOptions(
    options =>
    {
        options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.Strict;
    }
);

@martincostello
Copy link
Member

especially when the long value exceeds Int64.MaxValue.

If you don't want to use a string for a long, then how would you ever expect this to work?

The value would just wrap around and become negative (or throw an OverflowException). If you don't use negative values, you could try using ulong instead to double the value range.

@timeshift92
Copy link

I think it uses the maximum value of the long and not more than it, but when this value is passed through the OK() method
it rounds this value and for this reason the difference between the numeric value and the string is obtained

@Nodirbek-Abdulaxadov
Copy link
Author

The value would just wrap around and become negative (or throw an OverflowException). If you don't use negative values, you could try using ulong instead to double the value range.

Have you tried it yourself? It will be same result with ulong

@martincostello
Copy link
Member

martincostello commented May 7, 2024

I haven't, no, I'm just giving you suggestions.

@Nodirbek-Abdulaxadov
Copy link
Author

Nodirbek-Abdulaxadov commented Jun 3, 2024

Since this: [System.Text.Json] Serializer loses precision on ulong #52393 issue has been resolved, this issue has also been resolved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates
Projects
None yet
Development

No branches or pull requests

3 participants