Unity中Newtonsoft.Json高性能配置指南:解决IL2CPP、WebGL与移动端优化
2026/8/10 9:53:58 网站建设 项目流程

1. 项目概述:为什么Unity开发者需要一份Newtonsoft.Json的终极配置指南?

如果你在Unity项目里用过C#自带的JsonUtility,然后转头去用了Newtonsoft.Json(现在官方叫Json.NET),那你肯定懂那种“回不去了”的感觉。JsonUtility确实轻量,但功能也真是“轻”得让人头疼——不支持字典、不支持多态、序列化私有字段还得加个[SerializeField],更别提那些复杂的自定义类型转换了。Newtonsoft.Json几乎成了中大型Unity项目的标配,它强大、灵活,能处理你能想到的几乎所有JSON序列化场景。

但强大也带来了复杂性。直接把Newtonsoft.Json的DLL扔进Unity的Plugins文件夹,然后JsonConvert.SerializeObject就开干,是很多项目的起点,也是很多性能问题和诡异Bug的源头。我在多个Unity项目里,从手游到PC端游,都深度使用过这个库,踩过的坑数不胜数:WebGL平台初始化卡顿半分钟、移动端频繁GC导致卡顿、序列化循环引用直接栈溢出、以及因为配置不当导致的数据错乱。网上的资料零散且过时,很多是针对纯.NET环境的配置,在Unity这个“特立独行”的运行时里水土不服。

所以,这份“终极配置指南”的目的,不是简单地教你调用API,而是为你构建一个高性能、高稳定性、跨平台兼容的Newtonsoft.Json完整解决方案。我们会从Unity环境的特殊性讲起,深入到序列化设置的每一个参数,再到针对IL2CPP、WebGL、移动端的专项优化,最后提供一套开箱即用的配置模板和问题排查手册。无论你是正在为项目引入Newtonsoft.Json,还是已经在用它但被各种问题困扰,这篇文章都能让你彻底掌控这个强大的工具。

2. Newtonsoft.Json在Unity中的核心价值与常见陷阱

2.1 超越JsonUtility:我们究竟需要什么?

Unity内置的JsonUtility是一个基于Unity序列化系统的轻量级方案。它的优点是零外部依赖、GC压力小。但它的局限性在项目复杂度提升后会立刻显现:

  1. 类型支持有限:不直接支持Dictionary<TKey, TValue>,不支持多态(基类引用子类对象),对List<List<int>>这类嵌套泛型处理也可能有问题。
  2. 属性序列化:默认只序列化字段(且是public字段或带[SerializeField]的私有字段),不序列化属性(Property)。现代C#代码风格大量使用属性,这很不方便。
  3. 控制力弱:忽略空值、自定义日期格式、循环引用处理、命名策略(如驼峰命名)等高级功能统统没有。

Newtonsoft.Json恰好弥补了所有这些不足。它通过JsonPropertyJsonConverter等特性提供了极强的控制力。但正是这种灵活性,如果使用不当,在Unity环境下会放大问题。

2.2 Unity环境下的特殊挑战

Unity不是一个标准的.NET运行时。尤其是当你选择IL2CPP作为后端脚本编译方案时(这是发布到iOS、WebGL以及为了优化性能的常见选择),情况会变得复杂。

  • AOT编译限制:IL2CPP是一个提前编译(AOT)方案。这意味着所有代码必须在编译时就被确定。Newtonsoft.Json大量依赖反射动态代码生成(如DynamicMethod)来实现高性能序列化,这些在AOT环境下可能无法工作或导致运行时错误。
  • 链接器(Linker):为了减小包体,Unity会使用代码剥离(Code Stripping),移除“未使用”的代码。如果序列化/反序列化的类型没有被其他地方显式引用,链接器可能会误认为它们没用而将其删除,导致运行时出现“类型找不到”的异常。
  • WebGL的单线程:WebGL本质是单线程的。Newtonsoft.Json的某些默认设置或操作如果阻塞主线程过久,会导致页面卡死,这就是为什么你会遇到“Unity WebGL初始化很久”的问题之一——可能正卡在某个反射扫描或初始化里。
  • 移动端的GC压力:每一次new JsonSerializerSettings(),每一次字符串的创建和拼接,都在产生GC Alloc。在移动设备上,频繁的GC会引发帧率波动。

3. 高性能配置核心:深入解析JsonSerializerSettings

性能优化的核心在于理解和正确配置JsonSerializerSettings对象,并对其进行缓存。绝不要在每次序列化/反序列化时都创建新的设置实例。

3.1 必须缓存的全局设置实例

创建一个静态类来持有全局配置是最佳实践。

using Newtonsoft.Json; using UnityEngine; public static class JsonConfig { // 核心:缓存全局设置实例 public static readonly JsonSerializerSettings DefaultSettings = new JsonSerializerSettings { // 1. 格式化设置(调试用,发布时应关闭) Formatting = Formatting.None, // 发布时设为None以减少字符串体积 // 2. 空值处理 NullValueHandling = NullValueHandling.Ignore, // 3. 默认值处理 DefaultValueHandling = DefaultValueHandling.Ignore, // 4. 循环引用处理(谨慎使用!) ReferenceLoopHandling = ReferenceLoopHandling.Error, // 推荐先设为Error,发现循环引用再考虑Ignore或Serialize // 5. 类型名称处理(用于多态) TypeNameHandling = TypeNameHandling.None, // 安全考虑,默认关闭。需要时可用Auto或Objects // 6. 日期格式 DateFormatString = "yyyy-MM-ddTHH:mm:ss.fffZ", // ISO 8601格式 DateTimeZoneHandling = DateTimeZoneHandling.Utc, // 7. 浮点数特殊值处理(如NaN, Infinity) FloatFormatHandling = FloatFormatHandling.String, // 或 DefaultValueHandling.Ignore配合 FloatParseHandling = FloatParseHandling.Double, // 8. 合约解析器(影响属性/字段序列化规则) ContractResolver = new DefaultContractResolver { // 命名策略:驼峰命名 (myPropertyName -> myPropertyName) NamingStrategy = new CamelCaseNamingStrategy() }, // 9. 转换器集合(自定义序列化的核心) Converters = new List<JsonConverter>() { // 可以在这里添加全局自定义转换器,例如处理Unity的Vector3 // new Vector3Converter() } }; // 提供一个便捷的序列化方法 public static string SerializeObject(object value) { return JsonConvert.SerializeObject(value, DefaultSettings); } // 提供一个便捷的反序列化方法 public static T DeserializeObject<T>(string json) { return JsonConvert.DeserializeObject<T>(json, DefaultSettings); } }

关键参数解析:

  • NullValueHandling.Ignore&DefaultValueHandling.Ignore:这是减少JSON数据量的最有效手段之一。如果一个属性值是null或者等于该类型的默认值(如int的0,bool的false),它就不会被序列化到JSON字符串中。对于网络传输或存储,这能显著节省空间。
  • ReferenceLoopHandling这是新手最容易踩的坑。如果你的对象模型存在循环引用(例如,Player对象有一个Team属性,而Team对象又有一个List<Player>成员),默认设置Error会抛出异常,让你立刻发现问题。如果设为Ignore,它会默默跳过循环引用,可能导致数据丢失。如果错误地设为Serialize,则可能产生无限递归的JSON,导致栈溢出或生成巨大的字符串。最佳实践是始终先保持Error,在代码审查时杜绝循环引用设计。
  • TypeNameHandling:这个设置允许JSON中包含.NET类型名称(如"$type": "MyAssembly.MyClass, MyAssembly"),从而实现反序列化时还原到具体的子类。这是一个巨大的安全风险,如果反序列化的JSON来自不可信源(如网络请求),攻击者可能构造恶意$type来实例化任意类型,导致反序列化漏洞。在Unity中,除非你100%确定JSON来源可信且需要多态,否则永远设为None
  • ContractResolver:通过自定义IContractResolver,你可以完全控制哪些成员被序列化、它们的名称如何映射等。使用CamelCaseNamingStrategy可以使C#的帕斯卡命名属性(MyPropertyName)在JSON中变为驼峰命名(myPropertyName),这是JavaScript/JSON领域的常见约定。

3.2 自定义JsonConverter:处理特殊类型

Newtonsoft.Json的强大之处在于你可以为任何类型编写自定义的JsonConverter。在Unity中,这常用于处理Unity引擎特有的类型,如Vector3ColorQuaternion等,因为Newtonsoft.Json不知道如何序列化它们。

下面是一个Vector3Converter的例子:

using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; public class Vector3Converter : JsonConverter<Vector3> { public override void WriteJson(JsonWriter writer, Vector3 value, JsonSerializer serializer) { // 将Vector3序列化为一个简单的JSON对象 writer.WriteStartObject(); writer.WritePropertyName("x"); writer.WriteValue(value.x); writer.WritePropertyName("y"); writer.WriteValue(value.y); writer.WritePropertyName("z"); writer.WriteValue(value.z); writer.WriteEndObject(); } public override Vector3 ReadJson(JsonReader reader, Type objectType, Vector3 existingValue, bool hasExistingValue, JsonSerializer serializer) { // 从JSON对象中读取Vector3 JObject obj = JObject.Load(reader); return new Vector3( obj.Value<float>("x"), obj.Value<float>("y"), obj.Value<float>("z") ); } }

将这个转换器添加到全局设置的Converters列表中,那么项目中所有Vector3的序列化/反序列化都会自动使用它。

注意:自定义转换器需要谨慎处理嵌套和引用。对于简单的值类型(如Vector3)通常很安全。对于复杂的引用类型,要特别注意循环引用和状态管理。

4. 针对IL2CPP与代码剥离的生存指南

这是Unity项目使用Newtonsoft.Json最关键的环节,配置不当会导致在发布平台(尤其是iOS、WebGL)上崩溃。

4.1 解决AOT编译问题:使用预生成的序列化程序集

Newtonsoft.Json提供了一个名为“预生成的序列化程序集”的功能来应对AOT限制。原理是:在开发阶段(AOT编译之前),通过一个工具扫描你的代码,为所有需要通过Newtonsoft.Json序列化的类型提前生成序列化/反序列化的代码,从而避免运行时使用反射。

操作步骤:

  1. 获取工具:你需要使用Newtonsoft.Json提供的Newtonsoft.Json.ToolsNuGet包中的Newtonsoft.Json.Serialization.Aot工具,或者使用社区维护的Unity兼容版本。一个更常见的方法是使用一个名为“Unity Newtonsoft.Json AOT Support”的编辑器脚本。
  2. 创建Linker XML配置文件:在Assets目录下创建名为link.xml的文件。这个文件用于给Unity的代码剥离器(Linker)提供提示,告诉它哪些类型和程序集必须保留,即使它们看起来没有被直接引用。
    <?xml version="1.0" encoding="utf-8"?> <linker> <!-- 保留整个Newtonsoft.Json程序集 --> <assembly fullname="Newtonsoft.Json" preserve="all"/> <!-- 保留你自定义的、可能被反射使用的类型 --> <assembly fullname="Assembly-CSharp"> <!-- 保留所有以`Model`结尾的类,常用于数据模型 --> <type fullname="MyGame.Data.*" preserve="all"/> <!-- 保留特定命名空间下所有类型 --> <namespace fullname="MyGame.Networking" preserve="all"/> </assembly> </linker>
  3. 运行AOT预生成(如果工具支持):在Unity编辑器中,通过菜单项触发一个脚本,该脚本会遍历你的项目,找出所有被[JsonObject][JsonProperty]标记的或可能在集合中使用的类型,并为它们生成一个C#序列化器类文件(例如GeneratedSerializers.cs)。你需要将这个生成的文件包含在项目中一起编译。
  4. 注册生成的程序集:在游戏启动时(如Awake或静态构造函数中),你需要注册这个生成的程序集。
    using Newtonsoft.Json; using Newtonsoft.Json.Serialization; public class AotInitializer { [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void InitializeAot() { // 假设你的生成器创建了一个名为`GeneratedSerializers`的类 // 你需要根据实际工具生成的代码来调用注册方法 // 例如:GeneratedSerializers.RegisterAll(); // 更通用的方法是,如果你使用了预生成的合约解析器 DefaultContractResolver resolver = new MyPreGeneratedContractResolver(); JsonConfig.DefaultSettings.ContractResolver = resolver; } }

实操心得:对于中小型项目,有时仅仅依靠完善的link.xml配置文件,并确保所有可序列化类型都被显式引用(例如在某个List<T>声明中),就足以让IL2CPP正常工作。但对于类型繁多、动态性强的项目,预生成步骤几乎是必须的。务必在目标平台(如iOS、WebGL)的Development Build下进行充分测试,因为代码剥离只在发布构建时生效。

4.2 配置link.xml的黄金法则

link.xml是你的安全网。以下是一些经验法则:

  • 保留整个Newtonsoft.Json:最简单粗暴但有效,缺点是可能增加包体。对于移动端,可以尝试更精细的配置,但风险较高。
  • 保留所有数据模型:你的PlayerDataItemDataConfig等所有会被序列化的类所在的命名空间或程序集,都应该被preserve
  • 保留使用了特性标记的类型:任何使用了[JsonProperty][JsonConverter(typeof(...))]特性的类,都需要保留。
  • 测试!测试!测试!构建一个Development Build并运行,检查是否有TypeLoadExceptionMissingMethodException或序列化失败的错误。Unity的Player Log是排查这类问题的第一现场。

5. 实战优化:为WebGL与移动端量身定制

5.1 攻克WebGL初始化卡顿

“Unity WebGL初始化很久”这个问题,Newtonsoft.Json可能是元凶之一。其根本原因在于WebGL的单线程环境和JavaScript到C#的交互开销。

  • 罪魁祸首:首次使用的类型发现:当第一次序列化或反序列化某个类型时,Newtonsoft.Json会使用反射来扫描该类型的所有属性、字段,并构建一个“合约”(Contract)。这个过程是同步的,且可能非常耗时,尤其是对于拥有大量复杂类型的项目。在WebGL上,这个阻塞会直接导致页面“卡死”。
  • 解决方案:预热(Warm Up)思路很简单:在游戏加载初期(比如在Loading界面),在后台线程(WebGL虽然是单线程,但我们可以利用协程分帧)提前触发这些耗时的初始化。
    public static class JsonWarmUpper { // 定义所有你需要预热的根类型 private static readonly Type[] s_typesToWarmUp = new Type[] { typeof(PlayerData), typeof(Inventory), typeof(List<ItemData>), typeof(Dictionary<string, ConfigValue>), // ... 添加所有你的核心数据模型 }; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)] private static void Init() { // 在游戏开始时启动预热协程 // 需要挂载到一个不销毁的GameObject上执行 // 这里仅为示意 // WarmUpInBackground(); } private static IEnumerator WarmUpInBackground() { foreach (var type in s_typesToWarmUp) { // 关键操作:为每个类型创建一个空的实例并序列化/反序列化一次 // 这会强制Newtonsoft.Json为该类型生成并缓存合约 try { object instance = Activator.CreateInstance(type); string dummyJson = JsonConvert.SerializeObject(instance, JsonConfig.DefaultSettings); JsonConvert.DeserializeObject(dummyJson, type, JsonConfig.DefaultSettings); } catch (Exception e) { // 忽略一些无法实例化(如抽象类)或序列化的类型 Debug.LogWarning($"Warm-up failed for type {type.Name}: {e.Message}"); } // 每处理几个类型就yield一下,避免单帧卡顿 if (i % 5 == 0) { yield return null; } } Debug.Log("Newtonsoft.Json warm-up completed."); } }
    通过这种分帧预热,将不可避免的初始化开销分散到多帧中,从而避免在玩家第一次进行数据操作时遭遇明显的卡顿。

5.2 移动端GC优化实战

移动端对GC Alloc非常敏感。优化目标是将序列化操作产生的托管堆内存分配降至最低。

  1. 重用StringBuilder和JsonWriterJsonConvert.SerializeObject内部会创建StringBuilderJsonWriter。对于高频调用的序列化(如每帧序列化游戏状态用于同步),可以创建这些对象并重用。

    public class LowAllocJsonSerializer { private readonly StringBuilder _stringBuilder = new StringBuilder(256); private readonly StringWriter _stringWriter; private readonly JsonTextWriter _jsonWriter; public LowAllocJsonSerializer() { _stringWriter = new StringWriter(_stringBuilder); _jsonWriter = new JsonTextWriter(_stringWriter); } public string Serialize(object obj) { _stringBuilder.Clear(); JsonSerializer.CreateDefault(JsonConfig.DefaultSettings).Serialize(_jsonWriter, obj); return _stringBuilder.ToString(); } }

    注意:此方法非线程安全,每个线程需要自己的实例。对于大多数游戏逻辑,单线程使用一个全局实例即可。

  2. 使用流式API处理大JSON: 当需要处理非常大的JSON文件(如配置表)时,不要一次性将整个字符串读入内存(File.ReadAllText+DeserializeObject)。使用JsonTextReader进行流式读取。

    using (StreamReader file = File.OpenText("hugeConfig.json")) using (JsonTextReader reader = new JsonTextReader(file)) { JsonSerializer serializer = new JsonSerializer(); MyHugeConfig config = serializer.Deserialize<MyHugeConfig>(reader); }

    这种方式可以显著降低峰值内存占用。

  3. 谨慎使用Formatting.Indented: 美化格式(缩进)会产生大量的额外空格和换行符,显著增加字符串长度和内存分配。仅在开发调试时使用,发布版本务必设置为Formatting.None

6. 高级技巧与自定义配置

6.1 使用合约解析器(ContractResolver)实现精细控制

DefaultContractResolver是控制序列化行为的强大工具。你可以继承它并覆盖方法来实现自定义逻辑。

场景1:忽略所有带有某个自定义特性的属性。

public class IgnoreLoggingAttribute : Attribute { } public class IgnoreLoggingPropertiesResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); // 如果属性被标记了[IgnoreLogging],则忽略它 if (member.GetCustomAttribute<IgnoreLoggingAttribute>() != null) { property.Ignored = true; } // 或者,只读属性在序列化时忽略(反序列化不受影响) if (property.Readable && !property.Writable) { property.ShouldSerialize = instance => false; // 不序列化 } return property; } } // 使用 JsonConfig.DefaultSettings.ContractResolver = new IgnoreLoggingPropertiesResolver();

场景2:实现一个自定义的命名策略,比如所有属性名都转为大写。

public class UpperCaseNamingStrategy : NamingStrategy { protected override string ResolvePropertyName(string name) { return name.ToUpperInvariant(); } } // 使用 JsonConfig.DefaultSettings.ContractResolver = new DefaultContractResolver { NamingStrategy = new UpperCaseNamingStrategy() };

6.2 处理版本兼容性与缺失成员

当你的数据模型更新(添加或删除字段)后,如何保证旧版本序列化的JSON还能被新代码反序列化,或者新代码序列化的JSON能被旧版本程序部分识别?

  • MissingMemberHandling:设置为MissingMemberHandling.Error可以在反序列化时,如果JSON中有数据模型的类不存在的属性,则抛出异常。这有助于在开发早期发现字段名拼写错误。在发布版本中,可以考虑设为Ignore以增强兼容性。
  • NullValueHandling&DefaultValueHandling:如前所述,忽略空值和默认值,可以使新增字段在旧数据中不存在时,自动使用默认值,而不会出错。
  • 自定义JsonConverter处理版本迁移:对于重大的结构变更,可以编写一个转换器,在反序列化时,将旧格式的数据“翻译”成新格式的对象。

7. 常见问题排查与性能分析

7.1 问题排查速查表

问题现象可能原因解决方案
IL2CPP发布后崩溃,错误信息包含TypeLoadException,MissingMethodException代码剥离器删除了被反射使用的类型或方法。1. 检查并完善link.xml文件。
2. 确保所有可序列化类型都被显式引用。
3. 考虑使用AOT预生成序列化器。
WebGL平台初始化或首次序列化极慢首次类型反射开销在单线程上阻塞。1. 实现分帧的“预热”机制。
2. 审查数据模型,避免过于复杂的对象图。
移动端频繁GC,导致卡顿频繁创建JsonSerializerSettings、大字符串拼接、使用了Formatting.Indented1.缓存JsonSerializerSettings
2. 发布版本使用Formatting.None
3. 对于高频操作,考虑重用StringBuilderJsonWriter
4. 使用性能分析器(Profiler)定位具体分配源。
序列化循环引用导致栈溢出或数据异常对象模型存在双向引用(如父子关系)。1. 检查ReferenceLoopHandling设置(建议先保持Error)。
2. 重新设计数据模型,打破循环引用,例如使用ID引用代替直接对象引用。
3. 在特定属性上使用[JsonIgnore]忽略一方。
反序列化后数据为null或默认值JSON中的属性名与C#属性名不匹配(大小写、命名策略),或者该属性没有set访问器。1. 检查ContractResolver的命名策略。
2. 使用[JsonProperty("name_in_json")]显式指定映射。
3. 确保属性有public setter或标记了[JsonProperty]
包含特殊浮点值(NaN, Infinity)时序列化失败默认设置可能无法处理这些值。JsonSerializerSettings中设置FloatFormatHandling = FloatFormatHandling.StringFloatParseHandling = FloatParseHandling.Double
Unity特定类型(如Vector3)无法序列化Newtonsoft.Json不知道如何转换这些类型。为该类型编写自定义的JsonConverter,并添加到全局设置的Converters列表中。

7.2 性能分析建议

  1. 使用Unity Profiler:在Deep Profiling模式下,观察JsonConvert.SerializeObject/DeserializeObject调用产生的GC Alloc。这是衡量序列化开销最直接的指标。
  2. 基准测试:对于不同的配置(如开启/关闭缩进、使用不同的ContractResolver),对关键的数据结构进行序列化/反序列化的速度和内存分配测试。可以使用System.Diagnostics.Stopwatch
  3. 关注字符串操作:序列化的最终产物是字符串。大对象的序列化会产生巨大的字符串,其创建和垃圾回收成本很高。考虑是否真的需要全量序列化,或者是否可以拆分、增量更新。

8. 开箱即用配置模板与总结

最后,结合以上所有要点,这里提供一份我认为在大多数Unity项目中都比较稳健的“开箱即用”配置模板。你可以将它作为一个起点,根据项目具体需求进行调整。

// JsonManager.cs using System; using System.Collections.Generic; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using UnityEngine; public static class JsonManager { private static JsonSerializerSettings _settings; private static JsonSerializer _serializer; private static readonly StringBuilder _cachedStringBuilder = new StringBuilder(1024); private static readonly StringWriter _cachedStringWriter = new StringWriter(_cachedStringBuilder); public static JsonSerializerSettings Settings { get { if (_settings == null) { InitializeSettings(); } return _settings; } } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void Initialize() { // 确保设置被初始化 InitializeSettings(); // 可选:在这里进行预热(对于WebGL项目尤其重要) // WarmUpCriticalTypes(); Debug.Log("[JsonManager] Initialized with AOT/IL2CPP friendly settings."); } private static void InitializeSettings() { _settings = new JsonSerializerSettings { ContractResolver = new CustomContractResolver(), Converters = new List<JsonConverter> { // 添加你的自定义转换器,例如: // new Vector3Converter(), // new ColorConverter(), }, // 性能与兼容性 Formatting = Debug.isDebugBuild ? Formatting.Indented : Formatting.None, NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, // 发布版本建议Ignore以增强兼容性 ReferenceLoopHandling = ReferenceLoopHandling.Error, // 开发阶段严格报错 // 安全 TypeNameHandling = TypeNameHandling.None, // 永远不要自动开启 // 日期与浮点数 DateFormatString = "yyyy-MM-ddTHH:mm:ss.fffZ", DateTimeZoneHandling = DateTimeZoneHandling.Utc, FloatFormatHandling = FloatFormatHandling.String, FloatParseHandling = FloatParseHandling.Double, }; _serializer = JsonSerializer.CreateDefault(_settings); } // 高性能序列化(重用StringBuilder) public static string Serialize(object obj) { lock (_cachedStringBuilder) // 简单线程安全,如果多线程使用需考虑对象池 { _cachedStringBuilder.Clear(); _serializer.Serialize(new JsonTextWriter(_cachedStringWriter), obj); return _cachedStringBuilder.ToString(); } } // 标准序列化/反序列化 public static string SerializeObject(object value) => JsonConvert.SerializeObject(value, Settings); public static T DeserializeObject<T>(string json) => JsonConvert.DeserializeObject<T>(json, Settings); public static object DeserializeObject(string json, Type type) => JsonConvert.DeserializeObject(json, type, Settings); // 自定义合约解析器示例 private class CustomContractResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); // 示例:忽略所有名为“Password”的属性 if (property.PropertyName?.Contains("Password", StringComparison.OrdinalIgnoreCase) == true) { property.Ignored = true; } // 应用驼峰命名 property.PropertyName = GetCamelCaseName(property.PropertyName); return property; } private string GetCamelCaseName(string name) { if (string.IsNullOrEmpty(name) || !char.IsUpper(name[0])) return name; char[] chars = name.ToCharArray(); chars[0] = char.ToLowerInvariant(chars[0]); return new string(chars); } } }

配套的 link.xml (放置在 Assets 文件夹下)

<linker> <assembly fullname="Newtonsoft.Json" preserve="all"/> <assembly fullname="Assembly-CSharp"> <!-- 根据你的项目结构调整,保留数据模型所在的命名空间 --> <namespace fullname="MyGame.Data" preserve="all"/> <namespace fullname="MyGame.Config" preserve="all"/> <namespace fullname="MyGame.Networking" preserve="all"/> </assembly> <!-- 如果你有其他程序集,如 Assembly-CSharp-firstpass --> <assembly fullname="Assembly-CSharp-firstpass"> <namespace fullname="MyGame.Shared" preserve="all"/> </assembly> </linker>

这套配置模板集成了缓存、基础性能优化、IL2CPP兼容性提示和安全设置。真正的“终极”配置取决于你的项目具体需求。核心思想是:理解原理,按需配置,充分测试。尤其是在目标发布平台上的测试,是确保JSON序列化稳定可靠的最后一道,也是最重要的一道关卡。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询