Skip to content

Add AOT serialization support via configurable JsonSerializerOptions#2

Open
Claude wants to merge 4 commits intodevfrom
claude/fix-serialization-exception
Open

Add AOT serialization support via configurable JsonSerializerOptions#2
Claude wants to merge 4 commits intodevfrom
claude/fix-serialization-exception

Conversation

@Claude
Copy link
Copy Markdown

@Claude Claude AI commented Apr 7, 2026

Native AOT compilation disables reflection-based JSON serialization, causing System.InvalidOperationException at runtime when saving/loading profile configurations.

Changes

  • Added XFEProfile.JsonOptions property - Static JsonSerializerOptions property allowing AOT-compatible serialization via TypeInfoResolver
  • Updated all JSON serialization calls - All JsonSerializer.Serialize/Deserialize operations now use JsonOptions (affects XFEDictionaryLoadProfileOperation, XFEDictionarySaveProfileOperation, JsonLoadProfileOperation, JsonSaveProfileOperation, and obsolete methods)
  • Added ProfileJsonContext example - Source-generated JsonSerializerContext demonstrating AOT configuration in test project
  • Removed reflection workaround - Deleted JsonSerializerIsReflectionEnabledByDefault from test project in favor of proper AOT approach
  • Added AOT documentation - Comprehensive guide in AOT_SUPPORT.md with setup instructions and examples

Usage

For AOT compatibility, configure serialization at startup:

using System.Text.Json;
using System.Text.Json.Serialization;
using XFEExtension.NetCore.AutoConfig;

[JsonSerializable(typeof(YourDataType))]
[JsonSerializable(typeof(ProfileList<YourDataType>))]
public partial class AppJsonContext : JsonSerializerContext { }

// In Main or startup code
XFEProfile.JsonOptions = new JsonSerializerOptions
{
    TypeInfoResolver = AppJsonContext.Default
};

Non-AOT applications continue to work without configuration changes.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces configurable JsonSerializerOptions to enable AOT-compatible JSON serialization in XFEExtension.NetCore.AutoConfig, replacing reflection-dependent System.Text.Json defaults and documenting how to use source-generated contexts.

Changes:

  • Added XFEProfile.JsonOptions and routed JSON serialize/deserialize calls through it.
  • Updated the test project to demonstrate source-generated JsonSerializerContext usage and removed the reflection-enable workaround.
  • Added AOT_SUPPORT.md documentation describing AOT setup and troubleshooting.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
XFEExtension.NetCore.AutoConfig/XFEProfile.cs Adds global JSON options and updates serialization call sites to use them.
AutoConfig.Analyzer.Test/XFEExtension.NetCore.XUnit.Test.csproj Removes reflection-based JSON workaround for AOT.
AutoConfig.Analyzer.Test/Program.cs Configures XFEProfile.JsonOptions for AOT and uses source-generated type metadata in an example serialization.
AutoConfig.Analyzer.Test/ProfileJsonContext.cs Adds a source-generated JSON context example for AOT.
AOT_SUPPORT.md Adds documentation for configuring AOT-safe JSON serialization.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

var saveProfileDictionary = new XFEDictionary();
foreach (var property in propertyGetFuncDictionary)
saveProfileDictionary.Add(property.Key, JsonSerializer.Serialize(property.Value()));
saveProfileDictionary.Add(property.Key, JsonSerializer.Serialize(property.Value(), JsonOptions));
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XFEDictionarySaveProfileOperation currently serializes values via JsonSerializer.Serialize(property.Value(), JsonOptions). Because property.Value() is object?, this calls the Serialize<object> overload and uses metadata for object, which will not pick up source-generated metadata for the actual runtime type—breaking AOT even when JsonOptions.TypeInfoResolver is configured. Use the overload that supplies the declared type (e.g., look up propertyInfoDictionary[property.Key] and call Serialize(value, declaredType, JsonOptions)), with a safe fallback if the type is missing.

Suggested change
saveProfileDictionary.Add(property.Key, JsonSerializer.Serialize(property.Value(), JsonOptions));
{
var value = property.Value();
if (propertyInfoDictionary.TryGetValue(property.Key, out var declaredType))
saveProfileDictionary.Add(property.Key, JsonSerializer.Serialize(value, declaredType, JsonOptions));
else
saveProfileDictionary.Add(property.Key, JsonSerializer.Serialize(value, JsonOptions));
}

Copilot uses AI. Check for mistakes.
Comment on lines 15 to +18
/// <summary>
/// JSON序列化选项,用于配置序列化行为(支持AOT)
/// </summary>
public static JsonSerializerOptions JsonOptions { get; set; } = new JsonSerializerOptions();
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JsonOptions is publicly settable; if a consumer assigns null it will cause a NullReferenceException in every serialization call site. Consider guarding the setter (throw ArgumentNullException or coerce to a non-null default) using a backing field so JsonOptions is never null.

Suggested change
/// <summary>
/// JSON序列化选项,用于配置序列化行为(支持AOT)
/// </summary>
public static JsonSerializerOptions JsonOptions { get; set; } = new JsonSerializerOptions();
private static JsonSerializerOptions jsonOptions = new JsonSerializerOptions();
/// <summary>
/// JSON序列化选项,用于配置序列化行为(支持AOT)
/// </summary>
public static JsonSerializerOptions JsonOptions
{
get => jsonOptions;
set => jsonOptions = value ?? throw new ArgumentNullException(nameof(value));
}

Copilot uses AI. Check for mistakes.
Comment on lines 219 to 224
{
if (p.MemberInfo is FieldInfo fieldInfo)
return JsonSerializer.Serialize(fieldInfo.GetValue(i));
return JsonSerializer.Serialize(fieldInfo.GetValue(i), JsonOptions);
else if (p.MemberInfo is PropertyInfo propertyInfo)
return JsonSerializer.Serialize(propertyInfo.GetValue(i));
return JsonSerializer.Serialize(propertyInfo.GetValue(i), JsonOptions);
else
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The obsolete SaveProfilesFunc still serializes using JsonSerializer.Serialize(fieldInfo.GetValue(i), JsonOptions) / Serialize(propertyInfo.GetValue(i), JsonOptions), which again routes through the Serialize<object> overload and won’t use source-generated metadata for the member’s declared type in AOT. Prefer the overload that provides the member type (e.g., Serialize(value, fieldInfo.FieldType, JsonOptions) / Serialize(value, propertyInfo.PropertyType, JsonOptions)).

Copilot uses AI. Check for mistakes.

## 概述

从版本 2.0.8 开始,XFEExtension.NetCore.AutoConfig 支持 .NET Native AOT 编译。
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc claims AOT support is available “从版本 2.0.8 开始”, but the library project currently declares <Version>2.0.7</Version> in XFEExtension.NetCore.AutoConfig.csproj. Please align the documented version with the actual release/versioning plan (or bump the package version accordingly).

Suggested change
从版本 2.0.8 开始,XFEExtension.NetCore.AutoConfig 支持 .NET Native AOT 编译。
从版本 2.0.7 开始,XFEExtension.NetCore.AutoConfig 支持 .NET Native AOT 编译。

Copilot uses AI. Check for mistakes.
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.

3 participants