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

JsonSerializer throws serializing new object() with JsonSerializerDefaults.Web #61995

Closed
wannvmi opened this issue Nov 23, 2021 · 7 comments · Fixed by #62020
Closed

JsonSerializer throws serializing new object() with JsonSerializerDefaults.Web #61995

wannvmi opened this issue Nov 23, 2021 · 7 comments · Fixed by #62020
Assignees
Milestone

Comments

@wannvmi
Copy link

wannvmi commented Nov 23, 2021

Describe the bug

I found use return new object() will throw exception in .Net6 !

To Reproduce

this is Code
a minimal web API
Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapGet("/test", () => new object());

app.Run();

Exceptions

curl -X 'GET' 'http://localhost:5066/test' -H 'accept: application/json'

      An unhandled exception has occurred while executing the request.
      System.InvalidOperationException: Operation is not valid due to the current state of the object.
         at System.Text.Json.Serialization.JsonConverter`1.WriteNumberWithCustomHandling(Utf8JsonWriter writer, T value, JsonNumberHandling handling)
         at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.JsonSerializer.WriteCore[TValue](JsonConverter jsonConverter, Utf8JsonWriter writer, TValue& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
         at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
         at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.HttpResponseJsonExtensions.WriteAsJsonAsyncSlow[TValue](Stream body, TValue value, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.ExecuteObjectReturn(Object obj, HttpContext httpContext)
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
         at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
         at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Further technical details

  • ASP.NET Core version: 6.0.100
  • The IDE (VS / VS Code/ VS4Mac) you're running on, and its version: Visual Studio 2022 17.0.1
  • Include the output of dotnet --info:
    PS> dotnet --info
    .NET SDK (反映任何 global.json):
    Version: 6.0.100
    Commit: 9e8b04bbff

运行时环境:
OS Name: Windows
OS Version: 10.0.19043
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\6.0.100\

@halter73
Copy link
Member

This looks to be an issue with JsonSerializer when serializing object using JsonSerializerDefaults.Web.

using System.Text.Json;

// The following works:
await JsonSerializer.SerializeAsync(Stream.Null, new object(),
    new JsonSerializerOptions(JsonSerializerDefaults.General));

await JsonSerializer.SerializeAsync(Stream.Null, new { },
    new JsonSerializerOptions(JsonSerializerDefaults.Web));

// The following throws:
await JsonSerializer.SerializeAsync(Stream.Null, new object(),
    new JsonSerializerOptions(JsonSerializerDefaults.Web));

The above Program.cs results in the following output when built and run using the 6.0.100 SDK.

C:\dev\halter73\JsonSerializeObject> dotnet run
Unhandled exception. System.InvalidOperationException: Operation is not valid due to the current state of the object.
   at System.Text.Json.Serialization.JsonConverter`1.WriteNumberWithCustomHandling(Utf8JsonWriter writer, T value, JsonNumberHandling handling)
   at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.JsonSerializer.WriteCore[TValue](JsonConverter jsonConverter, Utf8JsonWriter writer, TValue& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
   at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
   at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
   at Program.<Main>$(String[] args) in C:\dev\halter73\JsonSerializeObject\Program.cs:line 11
   at Program.<Main>(String[] args)

@halter73 halter73 transferred this issue from dotnet/aspnetcore Nov 24, 2021
@dotnet-issue-labeler dotnet-issue-labeler bot added area-System.Text.Json untriaged New issue has not been triaged by the area owner labels Nov 24, 2021
@ghost
Copy link

ghost commented Nov 24, 2021

Tagging subscribers to this area: @dotnet/area-system-text-json
See info in area-owners.md if you want to be subscribed.

Issue Details

Describe the bug

I found use return new object() will throw exception in .Net6 !

To Reproduce

this is Code
a minimal web API
Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapGet("/test", () => new object());

app.Run();

Exceptions

curl -X 'GET' 'http://localhost:5066/test' -H 'accept: application/json'

      An unhandled exception has occurred while executing the request.
      System.InvalidOperationException: Operation is not valid due to the current state of the object.
         at System.Text.Json.Serialization.JsonConverter`1.WriteNumberWithCustomHandling(Utf8JsonWriter writer, T value, JsonNumberHandling handling)
         at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.JsonSerializer.WriteCore[TValue](JsonConverter jsonConverter, Utf8JsonWriter writer, TValue& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
         at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
         at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.HttpResponseJsonExtensions.WriteAsJsonAsyncSlow[TValue](Stream body, TValue value, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.ExecuteObjectReturn(Object obj, HttpContext httpContext)
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
         at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
         at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Further technical details

  • ASP.NET Core version: 6.0.100
  • The IDE (VS / VS Code/ VS4Mac) you're running on, and its version: Visual Studio 2022 17.0.1
  • Include the output of dotnet --info:
    PS> dotnet --info
    .NET SDK (反映任何 global.json):
    Version: 6.0.100
    Commit: 9e8b04bbff

运行时环境:
OS Name: Windows
OS Version: 10.0.19043
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\6.0.100\

Author: wannvmi
Assignees: halter73
Labels:

area-System.Text.Json, untriaged

Milestone: -

@halter73 halter73 changed the title ApiController return new object() Error in .Net6 JsonSerializer throws serializing new object() with JsonSerializerDefaults.Web Nov 24, 2021
@wannvmi
Copy link
Author

wannvmi commented Nov 24, 2021

NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.Strict can fix it.

using System.Text.Json;

// The following throws:
var res1 = JsonSerializer.Serialize(new object(), new JsonSerializerOptions(JsonSerializerDefaults.Web));
Console.WriteLine(res1);

// this works
var res2 = JsonSerializer.Serialize(new object(), new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
    NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.Strict
});
Console.WriteLine(res2);

the code in

public JsonSerializerOptions(JsonSerializerDefaults defaults) : this()

@halter73 halter73 removed their assignment Nov 24, 2021
@eiriktsarpalis eiriktsarpalis self-assigned this Nov 24, 2021
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Nov 24, 2021
@eiriktsarpalis
Copy link
Member

eiriktsarpalis commented Nov 24, 2021

Minimal reproduction:

using System.Text.Json;
using System.Text.Json.Serialization;

var options = new JsonSerializerOptions { NumberHandling = JsonNumberHandling.AllowReadingFromString };
JsonSerializer.Serialize(new object(), options);

This is a regression introduced by #54436. Changes in how polymorphic serialization for object instances is implemented means that serialized values specifically of type object (i.e. new object() instances) are handled by ObjectConverter rather than JsonConverterOfT.TryWrite. However, while ObjectConverter is overriding the ReadNumberWithCustomHandling method it does not override WriteNumberWithCustomHandling, resulting in the reported error when custom number handling is specified.

I've posted PR #62020 which fixes the problem. We might consider that change for servicing, however I find the new object() scenario to be a somewhat artificial corner case. Thoughts @ericstj @layomia?

@eiriktsarpalis eiriktsarpalis added this to the 7.0.0 milestone Nov 24, 2021
@eiriktsarpalis eiriktsarpalis added bug and removed untriaged New issue has not been triaged by the area owner labels Nov 24, 2021
@halter73
Copy link
Member

We might consider that change for servicing, however I find the new object() scenario to be a somewhat artificial corner case.

I agree it's a bit of a corner case, but this was found by an ASP.NET customer pretty quickly after releasing .NET 6. Getting this patched could save people some debugging in the future.

@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Nov 30, 2021
@ta264
Copy link
Contributor

ta264 commented Dec 1, 2021

+1 for a servicing change, this has been causing us a fair bit of grief. At least I now know the cause!

@ta264
Copy link
Contributor

ta264 commented Dec 1, 2021

Is there a workaround to get ASP to return an empty json response i.e. {}?

Edit: return new {}; seems to do the trick

ta264 added a commit to Readarr/Readarr that referenced this issue Dec 1, 2021
ta264 added a commit to Radarr/Radarr that referenced this issue Dec 1, 2021
ta264 added a commit to Readarr/Readarr that referenced this issue Dec 1, 2021
ta264 added a commit to Readarr/Readarr that referenced this issue Dec 2, 2021
ta264 added a commit to Radarr/Radarr that referenced this issue Dec 2, 2021
ta264 added a commit to Radarr/Radarr that referenced this issue Dec 2, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Dec 31, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants