Skip to content

chore: Support $(JsonSerializerIsReflectionEnabledByDefault)=false - #2996

Merged
kazo0 merged 1 commit into
mainfrom
dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection
Jan 9, 2026
Merged

chore: Support $(JsonSerializerIsReflectionEnabledByDefault)=false#2996
kazo0 merged 1 commit into
mainfrom
dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection

Conversation

@jonpryor

@jonpryor jonpryor commented Dec 23, 2025

Copy link
Copy Markdown
Contributor

Context: #2970
Context: #3001

Fixes: #2908

When $(JsonSerializerIsReflectionEnabledByDefault)=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
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() 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:

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, 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.

@jonpryor
jonpryor force-pushed the dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection branch 4 times, most recently from 8843670 to 0cfbbf8 Compare December 26, 2025 20:35
@jonpryor
jonpryor force-pushed the dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection branch 2 times, most recently from be7ade5 to 1973d60 Compare December 29, 2025 13:10
@jonpryor
jonpryor force-pushed the dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection branch 3 times, most recently from 488f026 to a59e9b7 Compare December 29, 2025 18:18
@jonpryor
jonpryor marked this pull request as ready for review December 29, 2025 19:26
@jonpryor
jonpryor force-pushed the dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection branch from a59e9b7 to 7d63351 Compare December 29, 2025 21:04
Comment thread doc/Learn/Serialization/HowTo-Serialization.md Outdated
@jonpryor

jonpryor commented Jan 6, 2026

Copy link
Copy Markdown
Contributor Author

Had a chat with @kazo0. Summary:

@jonpryor
jonpryor force-pushed the dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection branch 2 times, most recently from a64adcd to 19424ba Compare January 7, 2026 19:56
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
jonpryor force-pushed the dev/jonpryor/jonp-Uno.Extensions.Serialization-no-reflection branch from 19424ba to 6123458 Compare January 7, 2026 20:10
@jonpryor
jonpryor enabled auto-merge January 9, 2026 13:55
@kazo0
kazo0 disabled auto-merge January 9, 2026 13:57
@kazo0
kazo0 enabled auto-merge January 9, 2026 14:08
@github-actions

github-actions Bot commented Jan 9, 2026

Copy link
Copy Markdown

Azure Static Web Apps: Your stage site is ready! Visit it here: https://delightful-moss-0c5b8040f-2996.eastus2.azurestaticapps.net

@kazo0
kazo0 merged commit 5235975 into main Jan 9, 2026
18 of 19 checks passed
@kazo0
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.
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.
@agneszitte

Copy link
Copy Markdown
Member

/unobot prepare-release --commit-message-pattern "ci: Set version to '{0}'"

@unodevops

Copy link
Copy Markdown
Contributor

The release branch release/stable/7.1 has been created from ce51198 and the PR #3014 has been created for the version bump.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[.NET10] System.InvalidOperationException: JsonSerializerIsReflectionDisabled thrown with net10 and MSAL/OIDC on WASM

4 participants