用C# 11的列表模式实现一个解析JSON路径的递归函数
解读
国内Unity面试里,“JSON路径解析”是热更脚本、配置系统、网络协议落地的刚需。
题目表面考C# 11列表模式,实则同时验证三点:
- 对递归与不可变数据的驾驭能力——避免Unity主线程GC;
- 对System.Text.Json新API的熟悉度——Unity 2022 LTS已官方支持,替代Newtonsoft可减包体;
- 对Unity实际约束的敏感度——必须返回可空引用类型,且不能触发JIT限制(WebGL、iOS IL2CPP)。
因此,答案要兼顾语法糖、性能、Unity平台合规。
知识点
- C# 11列表模式(List Pattern):
[var head, .. var tail]在switch表达式里解构JsonElement数组,零堆分配。 - System.Text.Json.JsonElement:Unity 2022.3+内置,不依赖Newtonsoft,可直接读Span避免字符串拼接。
- 递归基例与尾递归:在IL2CPP下,编译器仍会把尾递归优化成循环,防止iOS栈溢出。
- 可空引用类型(#nullable enable):Unity 2021+正式支持,标记路径不存在时返回null,符合国内代码审计规范。
- 热更兼容:代码里不出现dynamic、反射Emit,确保ToLua、HybridCLR、ILRuntime都能直接翻译。
答案
#nullable enable
using System;
using System.Text.Json;
public static class JsonPath
{
/// <summary>
/// 用C# 11列表模式递归解析JSON路径
/// 示例:JsonPath.Resolve(root, "data", "0", "name")
/// </summary>
public static JsonElement? Resolve(JsonElement root, params ReadOnlySpan<string> path)
{
// 空路径直接返回根
if (path.IsEmpty) return root;
// 列表模式解构:head是首段,tail是剩余
switch (path)
{
// 递归基例:只剩一段
case [var head]:
return head switch
{
// 属性访问
_ when root.TryGetProperty(head, out var p) => p,
// 数组索引
_ when int.TryParse(head, out var idx) &&
root.ValueKind == JsonValueKind.Array &&
idx >= 0 && idx < root.GetArrayLength()
=> root[idx],
_ => null
};
// 递归步骤:先解head,再对tail递归
case [var head, .. var tail]:
var next = head switch
{
_ when root.TryGetProperty(head, out var p) => p,
_ when int.TryParse(head, out var idx) &&
root.ValueKind == JsonValueKind.Array &&
idx >= 0 && idx < root.GetArrayLength()
=> root[idx],
_ => default(JsonElement?)
};
return next.HasValue ? Resolve(next.Value, tail) : null;
// 兜底
default:
return null;
}
}
}
使用示例
var doc = JsonDocument.Parse(jsonText);
var name = JsonPath.Resolve(doc.RootElement, "config", "players", "0", "nickname");
Debug.Log(name?.GetString() ?? "路径不存在");
亮点
- 列表模式让路径解构一目了然,无substring/Split,零GC;
- **ReadOnlySpan<string>**传参,避免Unity主线程分配;
- 可空引用类型与TryGetProperty组合,符合国内代码规范。
拓展思考
- 性能极限:若路径深度>8,可改用迭代+Stack<ValueTuple>,把递归改循环,IL2CPP栈帧更稳。
- 出错信息:国内策划配表常写错路径,可返回自定义record struct JsonPathResult(JsonElement? Value, string? Error),日志直接打印Error,减少QA往返。
- Burst编译:若把JsonElement换成Unity.Mathematics.bool4等blittable结构,可让JobSystem+Burst在后台线程批量解析,主线程零开销。
- 热更兼容:ToLua不支持C# 11语法,预编译阶段用Source Generator把列表模式展开成if-else,热更DLL无感知。