启用System.Text.Json源代码生成器时序列化/反序列化随机失败排查
基于FNA框架(C#非Unity)开发包含复杂嵌套对象数据库的游戏,为适配Native AOT环境,计划从Newtonsoft.Json迁移至System.Text.Json。启用源代码生成器时,序列化/反序列化随机出现不完整现象(部分对象正常、部分异常);禁用生成器可正常序列化/反序列化,但无法适配Native AOT。
Newtonsoft.Json配置(Native AOT可运行但速度较慢)
public static JsonSerializerSettings settings = new JsonSerializerSettings { Formatting = Newtonsoft.Json.Formatting.None, TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto, DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.IgnoreAndPopulate, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace, Error = (sender, args) => { args.ErrorContext.Handled = true; Console.WriteLine("JsonDeserialize Error: ***" + args.ErrorContext.Error.Message); } };
System.Text.Json配置
public static JsonSerializerOptions jbinSerializerOption = new JsonSerializerOptions { WriteIndented = false, // 保持false以减小JSON体积 PropertyNameCaseInsensitive = false, DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault, // 忽略默认值减小体积 IncludeFields = true, ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles, TypeInfoResolver = Core_JsonContext.Default, // 注释此行则禁用源代码生成器 };
源代码生成器类定义
[JsonSourceGenerationOptions( // 源代码生成器参数 WriteIndented = false, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, GenerationMode = JsonSourceGenerationMode.Default, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, IncludeFields = true )] [JsonSerializable(typeof(Data_Zone))] [JsonSerializable(typeof(Data_PageCredit))] [JsonSerializable(typeof(AstralDBSystemLoader))] [JsonSerializable(typeof(AstralDataBaseLoader))] [JsonSerializable(typeof(Microsoft.Xna.Framework.Color))] [JsonSerializable(typeof(Microsoft.Xna.Framework.Rectangle))] [JsonSerializable(typeof(Microsoft.Xna.Framework.Vector2))] [JsonSerializable(typeof(Microsoft.Xna.Framework.Vector3))] [JsonSerializable(typeof(Microsoft.Xna.Framework.Vector4))] [JsonSerializable(typeof(CoreBD.Core_Bitmap))] [JsonSerializable(typeof(CoreBD.Core_Sprite))] [JsonSerializable(typeof(CoreBD.Core_Sequence))] [JsonSerializable(typeof(CoreBD.Core_Clip))] [JsonSerializable(typeof(CoreBD.Core_ClipEtatMouv))] [JsonSerializable(typeof(CoreBD.Core_ClipImgCle))] [JsonSerializable(typeof(CoreBD.Core_ClipRoute))] [JsonSerializable(typeof(CoreBD.Core_ClipImgCleAction))] [JsonSerializable(typeof(CoreBD.Core_IANeurone))] [JsonSerializable(typeof(CoreBD.Data_Zone))] // 重复注册了Data_Zone类型 [JsonSerializable(typeof(CoreBD.Core_ParamZone))] public partial class Core_JsonContext : JsonSerializerContext // 源代码生成器入口 { }
异常场景
执行以下克隆逻辑时,启用生成器会得到空对象或不完整对象:
string copieText = System.Text.Json.JsonSerializer.Serialize(source, jbinSerializerOptionTriche); return System.Text.Json.JsonSerializer.Deserialize<T>(copieText, jbinSerializerOption);
奇怪现象:无生成器序列化的数据库文件,可通过生成器正常反序列化,但运行时克隆场景失效。
1. 配置不一致问题
- 生成器与运行时配置不匹配:生成器配置中设置了
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,但运行时jbinSerializerOption未指定命名策略且PropertyNameCaseInsensitive = false,导致序列化用驼峰命名、反序列化严格匹配原属性名,最终丢失数据。 - 重复注册类型:生成器中重复添加
[JsonSerializable(typeof(Data_Zone))]和[JsonSerializable(typeof(CoreBD.Data_Zone))],可能导致生成器混淆,生成错误的序列化逻辑。
2. 多态类型处理缺失
Newtonsoft.Json通过TypeNameHandling.Auto自动处理多态类型,但System.Text.Json源代码生成器默认不支持多态。若嵌套对象包含多态结构(如基类引用子类实例),生成器无法识别子类类型,会导致序列化/反序列化不完整。
3. 克隆逻辑的配置差异
克隆代码中使用jbinSerializerOptionTriche序列化,却用jbinSerializerOption反序列化。若两个配置存在差异(如一个启用生成器、一个未启用,或命名策略不同),必然导致数据不匹配。
4. 对象创建与循环引用处理差异
Newtonsoft.Json的ObjectCreationHandling.Replace对应System.Text.Json的ReferenceHandler.IgnoreCycles,但生成器对复杂嵌套对象的循环引用处理需显式配置,否则可能出现对象创建异常。
修复步骤
- 统一配置参数
确保JsonSourceGenerationOptions与JsonSerializerOptions配置完全一致,同时删除重复的类型注册:
// 生成器配置 [JsonSourceGenerationOptions( WriteIndented = false, PropertyNamingPolicy = null, // 取消驼峰命名,与运行时配置对齐 GenerationMode = JsonSourceGenerationMode.Default, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, IncludeFields = true, ReferenceHandler = ReferenceHandler.IgnoreCycles // 添加循环引用处理,与运行时对齐 )] // 删除重复的[JsonSerializable(typeof(Data_Zone))]注册 // 运行时配置 public static JsonSerializerOptions jbinSerializerOption = new JsonSerializerOptions { WriteIndented = false, PropertyNameCaseInsensitive = false, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, IncludeFields = true, ReferenceHandler = ReferenceHandler.IgnoreCycles, TypeInfoResolver = Core_JsonContext.Default };
- 添加多态支持
对需要多态处理的基类,在生成器中显式声明子类类型:
// 示例:假设BaseClass是基类,Derived1、Derived2是子类 [JsonSerializable(typeof(BaseClass), TypeInfoPropertyName = "BaseClass")] [JsonDerivedType(typeof(Derived1), typeDiscriminator: "Derived1")] [JsonDerivedType(typeof(Derived2), typeDiscriminator: "Derived2")]
- 统一克隆逻辑的配置
确保序列化和反序列化使用相同的配置:
string copieText = System.Text.Json.JsonSerializer.Serialize(source, jbinSerializerOption); return System.Text.Json.JsonSerializer.Deserialize<T>(copieText, jbinSerializerOption);
检查成员可访问性
确保所有需要序列化的字段/属性为public,或用[JsonInclude]特性标记非公共成员。源代码生成器对非公共成员的处理需显式配置,灵活性低于反射模式。启用调试排查
添加调试日志或跟踪生成的序列化代码,查看是否有字段遗漏或错误:
- 可自定义转换器输出序列化过程的详细信息
- 查看生成的
Core_JsonContext代码,确认是否所有需要序列化的成员都被正确处理
内容的提问来源于stack exchange,提问作者fenryo237

