chore: Support $(JsonSerializerIsReflectionEnabledByDefault)=false - #2996
Merged
kazo0 merged 1 commit intoJan 9, 2026
Merged
Conversation
jonpryor
force-pushed
the
dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection
branch
4 times, most recently
from
December 26, 2025 20:35
8843670 to
0cfbbf8
Compare
jonpryor
force-pushed
the
dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection
branch
2 times, most recently
from
December 29, 2025 13:10
be7ade5 to
1973d60
Compare
7 tasks
jonpryor
force-pushed
the
dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection
branch
3 times, most recently
from
December 29, 2025 18:18
488f026 to
a59e9b7
Compare
jonpryor
marked this pull request as ready for review
December 29, 2025 19:26
7 tasks
jonpryor
force-pushed
the
dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection
branch
from
December 29, 2025 21:04
a59e9b7 to
7d63351
Compare
kazo0
reviewed
Jan 5, 2026
Contributor
Author
|
Had a chat with @kazo0. Summary:
|
jonpryor
force-pushed
the
dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection
branch
2 times, most recently
from
January 7, 2026 19:56
a64adcd to
19424ba
Compare
Context: #2970 Context: #3001 Fixes: #2908 When [`$(JsonSerializerIsReflectionEnabledByDefault)`][0]=false, then various `JsonSerializer` methods will throw. For example: partial class SerializerExtensionsTests { [TestMethod] public void ToFromStringTest() { var services = new ServiceCollection().BuildServiceProvider(); var Serializer = new SystemTextJsonSerializer(services); var classEntity = new SimpleClass { SimpleTextProperty = SimpleText + "Hello World!Class" }; var stringValue = Serializer.ToString(classEntity); // … } } Fails with: Failed ToFromStringTest [< 1 ms] Error Message: Test method Uno.Extensions.Serialization.Tests.SerializerExtensionsTests.ToFromStringTest threw exception: System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property. Stack Trace: at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled() at System.Text.Json.JsonSerializerOptions.ConfigureForJsonSerializer() at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions options, Type inputType) at System.Text.Json.JsonSerializer.Serialize(Object value, Type inputType, JsonSerializerOptions options) at Uno.Extensions.Serialization.SystemTextJsonSerializer.ToString(Object value, Type valueType) in /Volumes/Xamarin-Work/src/unoplatform/uno.extensions/src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs:line 101 at Uno.Extensions.Serialization.SerializerExtensions.ToString[T](ISerializer serializer, T value) in /Volumes/Xamarin-Work/src/unoplatform/uno.extensions/src/Uno.Extensions.Serialization/SerializerExtensions.cs:line 24 at Uno.Extensions.Serialization.Tests.SerializerExtensionsTests.ToFromStringTest() in /Volumes/Xamarin-Work/src/unoplatform/uno.extensions/src/Uno.Extensions.Serialization.Tests/StreamSerializerExtensionsTests.cs:line 66 at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr) The question: how should this work? How *can* it work? The [ASP.NET Core support for Native AOT > Changes to support source generation][1] documentation section provides some ideas for how this can work. The fundamental point: if Reflection based JSON (de)serialization cannot be used, and the suggested alternative is to use [System.Text.Json source generation][2], then there needs to be a way for the framework to use the generated code. In the ASP.NET Core docs, they suggest using the [`HttpJsonServiceExtensions.ConfigureHttpJsonOptions()`][3] extension method during service configuration: var builder = WebApplication.CreateSlimBuilder(args); builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); }); // … [JsonSerializable(typeof(Todo[]))] internal partial class AppJsonSerializerContext : JsonSerializerContext { } In order to work when `$(JsonSerializerIsReflectionEnabledByDefault)`=false, Uno.Extensions.Serialization needs a similar mechanism, a way to provide the generated `JsonSerializerContext` types to Uno.Extensions.Serialization. Draw inspiration from ASP.NET Core: * https://github.com/dotnet/dotnet/blob/9add30f0238eabacba9a92acec51a8d714fd1881/src/aspnetcore/src/Http/Http.Extensions/src/JsonOptions.cs * https://github.com/dotnet/dotnet/blob/9add30f0238eabacba9a92acec51a8d714fd1881/src/aspnetcore/src/Http/Http.Extensions/src/HttpJsonServiceExtensions.cs#L23-L27 * https://github.com/dotnet/dotnet/blob/9add30f0238eabacba9a92acec51a8d714fd1881/src/aspnetcore/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs#L397-L401 The core of the new mechanism *mirrors* what ASP.NET Core does: public partial class JsonSerializationOptions { public JsonSerializerOptions SerializerOptions { get; } } public partial class ServiceCollectionExtensions { public static IServiceCollection ConfigureJsonSerializationOptions(this IServiceCollection services, Action<SerializationOptions> configureOptions); public static IServiceCollection AddJsonSerialization(this IServiceCollection services, HostBuilderContext context, params IEnumerable<IJsonTypeInfoResolver> typeInfoResolvers); public static IServiceCollection AddJsonTypeInfo(this IServiceCollection services, params IEnumerable<IJsonTypeInfoResolver> typeInfoResolvers); } public partial class HostBuilderExtensions { public static IHostBuilder UseSerialization(this IHostBuilder hostBuilder, IEnumerable<IJsonTypeInfoResolver> typeInfoResolvers, Action<IServiceCollection> configure); public static IHostBuilder UseSerialization(this IHostBuilder hostBuilder, IEnumerable<IJsonTypeInfoResolver> typeInfoResolvers, Action<HostBuilderContext, IServiceCollection>? configure = default); } This allows adding the generated `*SerializationContext.Default` properties for subsequent use: hostBuilder.UseSerialization([AppJsonSerializerContext.Default]); To lead developers to the new APIs, the existing `IHostBuilder.UseSerialization()` and `IServiceCollection.AddSystemTextJsonSerialization()`, extension methods have been marked with `[RequiresDynamicCode]` and `[RequiresUnreferencedCode]`, as a way to notify developers of the new mechanism. This will result in build warnings: warning IL3050: Using member 'Uno.Extensions.HostBuilderExtensions.UseSerialization(IHostBuilder, Action<HostBuilderContext, IServiceCollection>)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. Default behavior requires Reflection. For trimming support, use: UseSerialization(IHostBuilder, IEnumerable<IJsonTypeInfoResolver>, Action<HostBuilderContext, IServiceCollection>). To *test* this, add a new `src/Uno.Extensions.Serialization.AotTests` test project which: 1. Sets `$(JsonSerializerIsReflectionEnabledByDefault)`=false, and 2. Defines `WITH_AOT_TRIMMING`, and 3. Imports all the C# source from `src/Uno.Extensions.Serialization.Tests`. The `WITH_AOT_TRIMMING` define allows us to use the same set of serialization tests for both untrimmed and trimmed environments. Update `stage-build-packages.yml` so that `*.AotTests.dll` are also executed by `VSTest@2`. Additionally, enable `$(IsAotCompatible)`=true for: * `src/Uno.Extensions.Serialization/Uno.Extensions.Serialization.csproj` Address the following warnings: src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(120,14): error IL2046: Member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.ToString(Object, Type)' with 'RequiresUnreferencedCodeAttribute' implements interface member 'Uno.Extensions.Serialization.ISerializer.ToString(Object, Type)' without 'RequiresUnreferencedCodeAttribute'. 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(88,16): error IL2046: Member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.ToString(Object, Type)' with 'RequiresUnreferencedCodeAttribute' implements interface member 'Uno.Extensions.Serialization.ISerializer.ToString(Object, Type)' without 'RequiresUnreferencedCodeAttribute'. 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(107,17): error IL2046: Member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.FromString(String, Type)' with 'RequiresUnreferencedCodeAttribute' implements interface member 'Uno.Extensions.Serialization.ISerializer.FromString(String, Type)' without 'RequiresUnreferencedCodeAttribute'. 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(120,14): error IL2046: Member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.FromString(String, Type)' with 'RequiresUnreferencedCodeAttribute' implements interface member 'Uno.Extensions.Serialization.ISerializer.FromString(String, Type)' without 'RequiresUnreferencedCodeAttribute'. 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(43,17): error IL2046: Member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.FromStream(Stream, Type)' with 'RequiresUnreferencedCodeAttribute' implements interface member 'Uno.Extensions.Serialization.ISerializer.FromStream(Stream, Type)' without 'RequiresUnreferencedCodeAttribute'. 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(120,14): error IL2046: Member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.FromStream(Stream, Type)' with 'RequiresUnreferencedCodeAttribute' implements interface member 'Uno.Extensions.Serialization.ISerializer.FromStream(Stream, Type)' without 'RequiresUnreferencedCodeAttribute'. 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(62,14): error IL2046: Member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.ToStream(Stream, Object, Type)' with 'RequiresUnreferencedCodeAttribute' implements interface member 'Uno.Extensions.Serialization.ISerializer.ToStream(Stream, Object, Type)' without 'RequiresUnreferencedCodeAttribute'. 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(120,14): error IL2046: Member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.ToStream(Stream, Object, Type)' with 'RequiresUnreferencedCodeAttribute' implements interface member 'Uno.Extensions.Serialization.ISerializer.ToStream(Stream, Object, Type)' without 'RequiresUnreferencedCodeAttribute'. 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(146,10): error IL2026: Using member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.FromStream(Stream, Type)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. From JsonDeserializer: JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(157,10): error IL2026: Using member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.ToString(Object, Type)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. From JsonDeserializer: JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(140,10): error IL2026: Using member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.FromString(String, Type)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. From JsonDeserializer: JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(168,3): error IL2026: Using member 'Uno.Extensions.Serialization.SystemTextJsonSerializer.ToStream(Stream, Object, Type)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. From JsonDeserializer: JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(46,89): error IL3050: Using member 'System.Text.Json.JsonSerializer.Deserialize(Stream, Type, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(71,4): error IL3050: Using member 'System.Text.Json.JsonSerializer.Serialize(Stream, Object, Type, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(91,85): error IL3050: Using member 'System.Text.Json.JsonSerializer.Serialize(Object, Type, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. src/Uno.Extensions.Serialization/SystemTextJsonSerializer.cs(110,89): error IL3050: Using member 'System.Text.Json.JsonSerializer.Deserialize(String, Type, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications. Suppress the above warnings. The foundation logic in `SystemTextJsonSerializer.cs` has been reworked to check [`JsonSerializer.IsReflectionEnabledByDefault`][4], which in turn should mirror the `$(JsonSerializerIsReflectionEnabledByDefault)` MSBuild property. This allows us to mirror System.Text.Json default behavior, attempting Reflection use when `JsonSerializer.IsReflectionEnabledByDefault` is true, otherwise throwing an `InvalidOperationException` stating how to address the problem: Reflection-based serialization has been disabled for this application. Use the IServiceCollection.AddJsonTypeInfoResolvers() or IHostBuilder.UseSerializationResolvers() extension methods to enable JSON deserialization for type `Example`. Which brings us to issue #2908: when a Uno app uses `.UseAuthentication()` when `$(JsonSerializerIsReflectionEnabledByDefault)`=false, the app may crash with: at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled() at System.Text.Json.JsonSerializerOptions.ConfigureForJsonSerializer() at System.Text.Json.JsonSerializerOptions.MakeReadOnly(Boolean populateMissingResolver) at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions options, Type inputType) at System.Text.Json.JsonSerializer.Serialize(Object value, Type inputType, JsonSerializerOptions options) at Uno.Extensions.Serialization.SystemTextJsonSerializer.ToString(Object value, Type valueType) at Uno.Extensions.Serialization.SerializerExtensions.ToString[String](ISerializer serializer, String value) at Uno.Extensions.Storage.KeyValueStorage.ApplicationDataKeyValueStorage.Serialize[String](String value) at Uno.Extensions.Storage.KeyValueStorage.ApplicationDataKeyValueStorage.<GetObjectValue>d__35`1[[System.String, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext() at Uno.Extensions.Storage.KeyValueStorage.ApplicationDataKeyValueStorage.<InternalSetAsync>d__36`1[[System.String, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext() at Uno.Extensions.Storage.KeyValueStorage.BaseKeyValueStorageWithCaching.<SetAsync>d__21`1[[System.String, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext() at Uno.Extensions.Authentication.TokenCache.SaveAsync(String provider, IDictionary`2 tokens, CancellationToken cancellation) at Uno.Extensions.Authentication.AuthenticationService.LoginAsync(IDispatcher dispatcher, IDictionary`2 credentials, String provider, Nullable`1 cancellationToken) at UnoApp140.Presentation.LoginModel.Login(CancellationToken token) in X:\src\TestApps\UnoApp140\UnoApp140\Presentation\LoginModel.cs:line 12 How do we fix this? Based on the above, we need a `.UseSerialization(…)` invocation which contains the `IJsonTypeInfoResolver` values which are needed to make the above work. The problem: what types need to be supported? This is "fun" because of generics: `.AddKeyedStorage()` registers an `IKeyValueStorage` implementation, and `TokenCache` uses `IKeyValueStorage.SetAsync<T>()`. `Uno.Extensions.Storage` *cannot* know which types that `TokenCache` will use; it needs to be told. Update `Uno.Extensions.Authentication` to have a new internal `TokenCacheContext` type which holds the `JsonTypeInfo<T>` values that it will use with `IKeyValueStorage.SetAsync<T>()`, and update `.UseAuthentication()` to call `.AddJsonSerialization(TokenCacheContext.Default)`, so that the required types are available at runtime. This fixes #2908. TODO: Uno.Extensions.Configuration uses Uno.Extensions.Serialization, but the types to support are not statically knowable. For example, `WritableOptions<T>` needs an `ISerializer<Dictionary<string, T>>`, but as `T` is provided by a `.Section<TSettingsOptions>()`, which is part of the *app*, there is no way for `Uno.Extensions.Configuration` to provide the required `JsonTypeInfo<TSettingsOptions>`. *A* thought is to provide a `.Section<TSettingsOptions>()` overload which has a `JsonTypeInfo<TSettingsOptions>` parameter, but this use case also overlaps with issue #3001. [0]: https://learn.microsoft.com/en-us/dotnet/core/compatibility/serialization/8.0/publishtrimmed [1]: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/native-aot?view=aspnetcore-10.0#changes-to-support-source-generation [2]: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation [3]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.httpjsonserviceextensions.configurehttpjsonoptions?view=aspnetcore-9.0 [4]: https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializer.isreflectionenabledbydefault?view=net-10.0
jonpryor
force-pushed
the
dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection
branch
from
January 7, 2026 20:10
19424ba to
6123458
Compare
kazo0
approved these changes
Jan 8, 2026
jonpryor
enabled auto-merge
January 9, 2026 13:55
kazo0
disabled auto-merge
January 9, 2026 13:57
kazo0
enabled auto-merge
January 9, 2026 14:08
|
Azure Static Web Apps: Your stage site is ready! Visit it here: https://delightful-moss-0c5b8040f-2996.eastus2.azurestaticapps.net |
kazo0
deleted the
dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection
branch
January 9, 2026 15:29
jonpryor
added a commit
to unoplatform/uno.chefs
that referenced
this pull request
Jan 13, 2026
Context: https://github.com/unoplatform/uno/issues/20533#issuecomment-3715971106 Context: unoplatform/uno.extensions#3008 Context: unoplatform/uno.extensions#2996 Uno 6.5.x has (very) preliminary support for NativeAOT! Let's try it: sed -i '' 's/"Uno.Sdk": ".*"/"Uno.Sdk": "6.5.0-dev.104"/g' global.json dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \ -bl Chefs/Chefs.csproj -p:SelfContained=true -p:UseSkiaRendering=true \ -p:PublishAot=true -p:IsAotCompatible=true Chefs/bin/Release/net10.0-desktop/osx-x64/publish/Chefs From the launch screen: * Click **Skip** * Click ** Sign in with Apple** * Click **❤️ Favorites** Result: no favorited recipes are shown. Worse, no useful error messages are written to the Terminal. What's wrong?! Part of what's wrong is unoplatform/uno.extensions#3008: the codepath that *should* be generating an error message writes the error message to a Dependency-Injection -originated `ILogger<BaseMockEndpoint>` instance, which only produces data if `.UseLogging()` is used, which was disabled for reasons nobody remembers. Re-enable `.UseLogging()`, and *now* we see why it fails: fail: Chefs.Client.Mock.BaseMockEndpoint[0] Failed to load SavedCookbooks.json System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property. at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled() + 0x27 at System.Text.Json.JsonSerializerOptions.ConfigureForJsonSerializer() + 0x2e at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions, Type) + 0x2d at System.Text.Json.JsonSerializer.Deserialize(String, Type, JsonSerializerOptions) + 0x1d at Uno.Extensions.Serialization.SerializerExtensions.FromString[T](ISerializer, String) + 0x39 at Chefs.Client.Mock.BaseMockEndpoint.<LoadData>d__3`1.MoveNext() + 0x144 Sort of. We see that it fails because we're missing `JsonTypeInfo<T>` for *something* that is required, but what? Enter unoplatform/uno.extensions#2996, which adds support for `$(JsonSerializerIsReflectionEnabledByDefault)`=false to Uno.Extensions.Serialization, providing a better error message: fail: Chefs.Client.Mock.BaseMockEndpoint[0] Failed to load SavedCookbooks.json System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Use the IServiceCollection.AddJsonTypeInfoResolvers() or IHostBuilder.UseSerializationResolvers() extension methods to enable JSON deserialization for type `System.Collections.Generic.List`1[[System.Guid, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]`. With a bit of squinting, we see that the type we need to support is `List<Guid>`. Update `MockEndpointContext` to support serializing `List<Guid>`: [JsonSerializable(typeof(List<Guid>))] partial class MockEndpointContext; Then update `ConfigureSerialization()` so that it's supported: services .AddJsonTypeInfo(MockEndpointContext.Default.ListGuid); This allows `List<Guid>` to be properly deserialized, which in turn allows **❤️ Favorites** to actually show favorited items.
7 tasks
jonpryor
added a commit
to unoplatform/uno.chefs
that referenced
this pull request
Jan 14, 2026
Context: https://github.com/unoplatform/uno/issues/20533#issuecomment-3715971106 Context: unoplatform/uno.extensions#3008 Context: unoplatform/uno.extensions#2996 Uno 6.5.x has (very) preliminary support for NativeAOT! Let's try it: sed -i '' 's/"Uno.Sdk": ".*"/"Uno.Sdk": "6.5.0-dev.104"/g' global.json dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \ -bl Chefs/Chefs.csproj -p:SelfContained=true -p:UseSkiaRendering=true \ -p:PublishAot=true -p:IsAotCompatible=true Chefs/bin/Release/net10.0-desktop/osx-x64/publish/Chefs From the launch screen: * Click **Skip** * Click ** Sign in with Apple** * Click **❤️ Favorites** Result: no favorited recipes are shown. Worse, no useful error messages are written to the Terminal. What's wrong?! Part of what's wrong is unoplatform/uno.extensions#3008: the codepath that *should* be generating an error message writes the error message to a Dependency-Injection -originated `ILogger<BaseMockEndpoint>` instance, which only produces data if `.UseLogging()` is used, which was disabled for reasons nobody remembers. Re-enable `.UseLogging()`, and *now* we see why it fails: fail: Chefs.Client.Mock.BaseMockEndpoint[0] Failed to load SavedCookbooks.json System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property. at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled() + 0x27 at System.Text.Json.JsonSerializerOptions.ConfigureForJsonSerializer() + 0x2e at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions, Type) + 0x2d at System.Text.Json.JsonSerializer.Deserialize(String, Type, JsonSerializerOptions) + 0x1d at Uno.Extensions.Serialization.SerializerExtensions.FromString[T](ISerializer, String) + 0x39 at Chefs.Client.Mock.BaseMockEndpoint.<LoadData>d__3`1.MoveNext() + 0x144 Sort of. We see that it fails because we're missing `JsonTypeInfo<T>` for *something* that is required, but what? Enter unoplatform/uno.extensions#2996, which adds support for `$(JsonSerializerIsReflectionEnabledByDefault)`=false to Uno.Extensions.Serialization, providing a better error message: fail: Chefs.Client.Mock.BaseMockEndpoint[0] Failed to load SavedCookbooks.json System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Use the IServiceCollection.AddJsonTypeInfoResolvers() or IHostBuilder.UseSerializationResolvers() extension methods to enable JSON deserialization for type `System.Collections.Generic.List`1[[System.Guid, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]`. With a bit of squinting, we see that the type we need to support is `List<Guid>`. Update `MockEndpointContext` to support serializing `List<Guid>`: [JsonSerializable(typeof(List<Guid>))] partial class MockEndpointContext; Then update `ConfigureSerialization()` so that it's supported: services .AddJsonTypeInfo(MockEndpointContext.Default.ListGuid); This allows `List<Guid>` to be properly deserialized, which in turn allows **❤️ Favorites** to actually show favorited items.
kazo0
pushed a commit
to unoplatform/uno.chefs
that referenced
this pull request
Jan 15, 2026
Context: https://github.com/unoplatform/uno/issues/20533#issuecomment-3715971106 Context: unoplatform/uno.extensions#3008 Context: unoplatform/uno.extensions#2996 Uno 6.5.x has (very) preliminary support for NativeAOT! Let's try it: sed -i '' 's/"Uno.Sdk": ".*"/"Uno.Sdk": "6.5.0-dev.104"/g' global.json dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \ -bl Chefs/Chefs.csproj -p:SelfContained=true -p:UseSkiaRendering=true \ -p:PublishAot=true -p:IsAotCompatible=true Chefs/bin/Release/net10.0-desktop/osx-x64/publish/Chefs From the launch screen: * Click **Skip** * Click ** Sign in with Apple** * Click **❤️ Favorites** Result: no favorited recipes are shown. Worse, no useful error messages are written to the Terminal. What's wrong?! Part of what's wrong is unoplatform/uno.extensions#3008: the codepath that *should* be generating an error message writes the error message to a Dependency-Injection -originated `ILogger<BaseMockEndpoint>` instance, which only produces data if `.UseLogging()` is used, which was disabled for reasons nobody remembers. Re-enable `.UseLogging()`, and *now* we see why it fails: fail: Chefs.Client.Mock.BaseMockEndpoint[0] Failed to load SavedCookbooks.json System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property. at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled() + 0x27 at System.Text.Json.JsonSerializerOptions.ConfigureForJsonSerializer() + 0x2e at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions, Type) + 0x2d at System.Text.Json.JsonSerializer.Deserialize(String, Type, JsonSerializerOptions) + 0x1d at Uno.Extensions.Serialization.SerializerExtensions.FromString[T](ISerializer, String) + 0x39 at Chefs.Client.Mock.BaseMockEndpoint.<LoadData>d__3`1.MoveNext() + 0x144 Sort of. We see that it fails because we're missing `JsonTypeInfo<T>` for *something* that is required, but what? Enter unoplatform/uno.extensions#2996, which adds support for `$(JsonSerializerIsReflectionEnabledByDefault)`=false to Uno.Extensions.Serialization, providing a better error message: fail: Chefs.Client.Mock.BaseMockEndpoint[0] Failed to load SavedCookbooks.json System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Use the IServiceCollection.AddJsonTypeInfoResolvers() or IHostBuilder.UseSerializationResolvers() extension methods to enable JSON deserialization for type `System.Collections.Generic.List`1[[System.Guid, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]`. With a bit of squinting, we see that the type we need to support is `List<Guid>`. Update `MockEndpointContext` to support serializing `List<Guid>`: [JsonSerializable(typeof(List<Guid>))] partial class MockEndpointContext; Then update `ConfigureSerialization()` so that it's supported: services .AddJsonTypeInfo(MockEndpointContext.Default.ListGuid); This allows `List<Guid>` to be properly deserialized, which in turn allows **❤️ Favorites** to actually show favorited items.
Member
|
/unobot prepare-release --commit-message-pattern "ci: Set version to '{0}'" |
Contributor
14 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context: #2970
Context: #3001
Fixes: #2908
When
$(JsonSerializerIsReflectionEnabledByDefault)=false,then various
JsonSerializermethods will throw.For example:
Fails with:
The question: how should this work? How can it work?
The ASP.NET Core support for Native AOT > Changes to support source generation
documentation section provides some ideas for how this can work.
The fundamental point: if Reflection based JSON (de)serialization
cannot be used, and the suggested alternative is to use
System.Text.Json source generation, then there needs to be a way
for the framework to use the generated code.
In the ASP.NET Core docs, they suggest using the
HttpJsonServiceExtensions.ConfigureHttpJsonOptions()extensionmethod during service configuration:
In order to work when
$(JsonSerializerIsReflectionEnabledByDefault)=false,Uno.Extensions.Serialization needs a similar mechanism, a way to
provide the generated
JsonSerializerContexttypes toUno.Extensions.Serialization.
Draw inspiration from ASP.NET Core:
The core of the new mechanism mirrors what ASP.NET Core does:
This allows adding the generated
*SerializationContext.Defaultproperties for subsequent use:
To lead developers to the new APIs, the existing
IHostBuilder.UseSerialization()andIServiceCollection.AddSystemTextJsonSerialization(), extensionmethods have been marked with
[RequiresDynamicCode]and[RequiresUnreferencedCode], as a way to notify developers of thenew mechanism. This will result in build warnings:
To test this, add a new
src/Uno.Extensions.Serialization.AotTeststest project which:
$(JsonSerializerIsReflectionEnabledByDefault)=false, andWITH_AOT_TRIMMING, andsrc/Uno.Extensions.Serialization.Tests.The
WITH_AOT_TRIMMINGdefine allows us to use the same set ofserialization tests for both untrimmed and trimmed environments.
Update
stage-build-packages.ymlso that*.AotTests.dllare alsoexecuted by
VSTest@2.Additionally, enable
$(IsAotCompatible)=true for:src/Uno.Extensions.Serialization/Uno.Extensions.Serialization.csprojAddress the following warnings:
Suppress the above warnings. The foundation logic in
SystemTextJsonSerializer.cshas been reworked to checkJsonSerializer.IsReflectionEnabledByDefault, which in turnshould mirror the
$(JsonSerializerIsReflectionEnabledByDefault)MSBuild property. This allows us to mirror System.Text.Json default
behavior, attempting Reflection use when
JsonSerializer.IsReflectionEnabledByDefaultis true, otherwisethrowing an
InvalidOperationExceptionstating how to address theproblem:
Which brings us to issue #2908: when a Uno app uses
.UseAuthentication()when$(JsonSerializerIsReflectionEnabledByDefault)=false, the appmay crash with:
How do we fix this? Based on the above, we need a
.UseSerialization(…)invocation which contains theIJsonTypeInfoResolvervalues which are needed to make the above work.The problem: what types need to be supported? This is "fun" because
of generics:
.AddKeyedStorage()registers anIKeyValueStorageimplementation, and
TokenCacheusesIKeyValueStorage.SetAsync<T>().Uno.Extensions.Storagecannot know which types thatTokenCachewill use; it needs to be told.
Update
Uno.Extensions.Authenticationto have a new internalTokenCacheContexttype which holds theJsonTypeInfo<T>valuesthat it will use with
IKeyValueStorage.SetAsync<T>(), and update.UseAuthentication()to call.AddJsonSerialization(TokenCacheContext.Default), so that therequired types are available at runtime. This fixes #2908.
TODO: Uno.Extensions.Configuration uses Uno.Extensions.Serialization,
but the types to support are not statically knowable. For example,
WritableOptions<T>needs anISerializer<Dictionary<string, T>>,but as
Tis provided by a.Section<TSettingsOptions>(), which ispart of the app, there is no way for
Uno.Extensions.Configurationto provide the required
JsonTypeInfo<TSettingsOptions>.A thought is to provide a
.Section<TSettingsOptions>()overloadwhich has a
JsonTypeInfo<TSettingsOptions>parameter, but this usecase also overlaps with issue #3001.