You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Linq.Expression获取Newtonsoft.Json序列化对应JsonPointer路径

实现方案

可以实现,核心逻辑分为两步:

  1. 解析传入的Lambda表达式,提取从根类到目标属性的访问层级链
  2. 用指定的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 10:54:03