-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Add source gen support for unspeakable types
#82457
Comments
Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis Issue DetailsDescription
While it works correctly when the
Reproduction Stepsvar builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.TypeInfoResolver = MyContext.Default);
var app = builder.Build();
app.MapGet("/foo", Foo);
async IAsyncEnumerable<int> Foo()
{
foreach (var i in Enumerable.Range(0, 10))
{
await Task.Delay(1000);
yield return i;
}
}
[JsonSerializable(typeof(IAsyncEnumerable<int>))]
internal partial class MyContext : JsonSerializerContext
{} Expected behaviorWhile is possible to have some additional logic on Eg.: Current source-gen`ed context public override global::System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(global::System.Type type)
{
if (type == typeof(global::System.Collections.Generic.IAsyncEnumerable<global::System.Int32>))
{
return this.IAsyncEnumerableInt32;
}
return null;
} Expected public override global::System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(global::System.Type type)
{
if (type == typeof(global::System.Collections.Generic.IAsyncEnumerable<global::System.Int32>))
{
return this.IAsyncEnumerableInt32;
}
if (type.IsAssignableFrom(typeof(global::System.Collections.Generic.IAsyncEnumerable<global::System.Int32>)))
{
return this.IAsyncEnumerableInt32;
}
return null;
} Actual behavior
Regression?No response Known WorkaroundsNo response ConfigurationNo response Other informationNo response
|
For contrast, this is how the source generator produces metadata for IAsyncEnumerable's implementations that are visible to the source generator: [JsonSerializable(typeof(MyAsyncEnumerable))]
public partial class MyContext : JsonSerializerContext
{
}
public class MyAsyncEnumerable : IAsyncEnumerable<int>
{
IAsyncEnumerator<int> IAsyncEnumerable<int>.GetAsyncEnumerator(CancellationToken cancellationToken)
=> throw new NotImplementedException();
} Produces jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateIAsyncEnumerableInfo<global::MyAsyncEnumerable, global::System.Int32>(options, info); It's important to note that we wouldn't be able produce equivalent metadata for private implementations without resorting to Lines 28 to 29 in cb8d49a
One possible solution is that we use nearest ancestor resolution logic and fall back to the closest available supertype. This fallback should only happen in serialization as generally speaking it could only be supported in contravariant methods. We should never be returning the
We also need to be careful about leaving out fallback to trivial supertypes such as |
Why is ASP.NET looking at the runtime type when calling JSON? It should be using return type directly and then the metadata for unspeakable type wouldn't be needed... Am I missing something here? |
Short answer is filters. They can return |
should they be special casing that on their side then? |
They could, although it would be replicating the logic that the PR is introducing. And, as mentioned in #83631 (comment) the issue is not specific to boxing, it also addresses the issue of private interface implementation support. cc @eerhardt |
Note, ASP.NET also looks at the runtime type (today) in order to get the intended polymorphism serialization behavior. See: That should hopefully no longer be needed once #77532 is addressed.
How would ASP.NET special case it? It only has a single type - the runtime type. There is no "declared type" in filters. The "declared type" is |
And it's not just impacting ASP.NET obviously, we've had our own share of issues filed about this: #79661 |
Reopening to track potential changes in the semantics of the feature (enabling via feature flags, etc.) |
Description
Minimal APIs
uses the runtime type to call the serializer in most of the scenarios, that means once I have a compiler generated type (unspeakable type
) returning from my API, this type will be used for serialization.While it works correctly when the
reflection-based resolver
is turned on, I could not find a way to add this compiler generated type to my source-generatedJsonSerializerContext
. Even if I try to specify the interface implemented by type, eg.:IAsyncEnumerable
and still getting the following error:Metadata for type 'Program+<<<Main>$>g__Foo|0_5>d' was not provided by TypeInfoResolver of type 'MyContext'. If using source generation, ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.
Reproduction Steps
Expected behavior
While is possible to have some additional logic on
ASP.NET Core
side, to check some sort of known types, the expected behavior is to have the metadata resolution be able to detect, as a fallback in the source generator context, if the requested type implements one of the interfaces registered in the context.Eg.:
Current source-gen`ed context
Expected
Actual behavior
Regression?
No response
Known Workarounds
No response
Configuration
No response
Other information
No response
The text was updated successfully, but these errors were encountered: