如何通过Linq.Expression获取Newtonsoft.Json序列化对应JsonPointer路径
实现方案
可以实现,核心逻辑分为两步:
- 解析传入的Lambda表达式,提取从根类到目标属性的访问层级链
- 用指定的Newtonsoft.Json契约解析器,将每个层级的属性名转为序列化时实际输出的JSON字段名,拼接为JsonPointer格式路径
完整实现代码
using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System.Linq.Expressions; using System.Reflection; public static class JsonPointerHelper { public static string GetJsonPointer<T>(Expression<Func<T, object>> expression, IContractResolver contractResolver) { var pathSegments = new List<string>(); var currentExpression = expression.Body; // 处理表达式外层的隐式类型转换(比如值类型转object的装箱) if (currentExpression is UnaryExpression unaryExp) { currentExpression = unaryExp.Operand; } // 逐层拆解属性访问表达式 while (currentExpression is MemberExpression memberExp) { if (memberExp.Member is PropertyInfo property) { // 用契约解析器获取序列化时的实际属性名 var contract = (JsonObjectContract)contractResolver.ResolveContract(memberExp.Expression.Type); var jsonProperty = contract.Properties.First(p => p.UnderlyingName == property.Name); pathSegments.Insert(0, jsonProperty.PropertyName); } currentExpression = memberExp.Expression; } // 拼接为JsonPointer格式 return "/" + string.Join("/", pathSegments); } // 重载支持无返回值的表达式,避免值类型装箱转换的额外处理 public static string GetJsonPointer<T, TProperty>(Expression<Func<T, TProperty>> expression, IContractResolver contractResolver) { var pathSegments = new List<string>(); var currentExpression = expression.Body; while (currentExpression is MemberExpression memberExp) { if (memberExp.Member is PropertyInfo property) { var contract = (JsonObjectContract)contractResolver.ResolveContract(memberExp.Expression.Type); var jsonProperty = contract.Properties.First(p => p.UnderlyingName == property.Name); pathSegments.Insert(0, jsonProperty.PropertyName); } currentExpression = memberExp.Expression; } return "/" + string.Join("/", pathSegments); } }
测试验证
用你提供的示例测试:
public class Foo { public string Bar { get; set; } } // 调用示例 var path = JsonPointerHelper.GetJsonPointer<Foo>(x => x.Bar, new CamelCasePropertyNamesContractResolver()); // 输出结果为 /bar,符合预期
扩展支持
如果需要支持数组/列表索引的场景,只需要在拆解表达式时增加对IndexExpression的处理,提取索引值加入路径片段即可,示例效果如下:
// 嵌套属性+索引测试用例 public class Foo { public List<Bar> Bars { get; set; } } public class Bar { public string Name { get; set; } } // 调用后返回 /bars/0/name var path = JsonPointerHelper.GetJsonPointer<Foo>(x => x.Bars[0].Name, new CamelCasePropertyNamesContractResolver());
内容的提问来源于stack exchange,提问作者Arithmomaniac
相关产品推荐
相关产品推荐

