来自 Linq 表达式的 JsonPointer
JsonPointer from Linq Expression
有没有办法获取给定 Linq.Expression 的 JsonPointer,因为它会被给定的 Newtonsoft.Json 合同解析器序列化?
例如
public class Foo { public string Bar { get; set; } }
var path = GetJsonPointer<Foo>(x => x.Bar, new CamelCasePropertyNamesContractResolver())
//how to write GetJsonPointer so that "path" would equal "/bar"?
编辑:
对于 json 指针,查看 JsonPointer.Net。它是新套件的一部分,在 System.Text.Json.
上运行
既然你想要一个指针,那么使用 Newtonsoft 并不重要。使用 linq 生成指针,调用 .ToString()
,然后您可以使用 Newtonsoft 的结果。
// get a pointer
var pointer = JsonPointer.Create<Foo>(x => x.Bar);
var asString = pointer.ToString();
// use the pointer
暂时不支持自定义大小写;它使用模型的外壳。但这是一个很好的功能创意,我可以相当简单地添加它。它将使用 System.Text.Json 机制而不是 Newtonsoft。
有关详细信息,请参阅 the docs。
对于json路径:
看看Manatee.Json。它绝对可以做到。
它是我 json-everything suite 的前身。
现在已弃用它以支持我的新库,但我尚未将此特定功能添加到新库中。
这里最棘手的部分是获取 属性 的名称,因为它将被序列化。您可以通过以下方式进行操作:
static string GetNameUnderContract(IContractResolver resolver, MemberInfo member)
{
var contract = (JsonObjectContract)resolver.ResolveContract(member.DeclaringType);
var property = contract.Properties.Single(x => x.UnderlyingName == member.Name);
return property.PropertyName;
}
一旦你有了它,你就可以处理表达式的每个级别并将结果弹出到字符串堆栈中。除了简单的成员访问之外,以下快速实现还支持索引。
public string GetJsonPointer<T>(IContractResolver resolver, Expression<Func<T,object>> expression)
{
Stack<string> pathParts = new();
var currentExpression = expression.Body;
while (currentExpression is not ParameterExpression)
{
if (currentExpression is MemberExpression memberExpression)
{
// Member access: fetch serialized name and pop
pathParts.Push(GetNameUnderContract(memberExpression.Member));
currentExpression = memberExpression.Expression;
}
else if (
currentExpression is BinaryExpression binaryExpression and { NodeType: ExpressionType.ArrayIndex }
&& binaryExpression.Right is ConstantExpression arrayIndexConstantExpression
)
{
// Array index
pathParts.Push(arrayIndexConstantExpression.Value.ToString());
currentExpression = binaryExpression.Left;
}
else if (
currentExpression is MethodCallExpression callExpression and { Arguments: { Count: 1 }, Method: { Name: "get_Item" } }
&& callExpression.Arguments[0] is ConstantExpression listIndexConstantExpression and { Type: { Name: nameof(System.Int32) } }
&& callExpression.Method.DeclaringType.GetInterfaces().Any(i=>i. IsGenericType && i.GetGenericTypeDefinition()==typeof(IReadOnlyList<>))
)
{
// IReadOnlyList index of other type
pathParts.Push(listIndexConstantExpression.Value);
currentExpression = callExpression.Object;
}
else
{
throw new InvalidOperationException($"{currentExpression.GetType().Name} (at {currentExpression}) not supported");
}
}
return string.Join("/", pathParts);
}
调用示例:
public record Foo([property: JsonProperty("Barrrs")] Bar[] Bars);
public record Bar(string Baz);
var resolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() };
GetJsonPointer<Foo>(resolver, x => x.Bars[0].Baz).Dump();
//dumps "/Barrrs[0]/baz"
有没有办法获取给定 Linq.Expression 的 JsonPointer,因为它会被给定的 Newtonsoft.Json 合同解析器序列化?
例如
public class Foo { public string Bar { get; set; } }
var path = GetJsonPointer<Foo>(x => x.Bar, new CamelCasePropertyNamesContractResolver())
//how to write GetJsonPointer so that "path" would equal "/bar"?
编辑:
对于 json 指针,查看 JsonPointer.Net。它是新套件的一部分,在 System.Text.Json.
上运行既然你想要一个指针,那么使用 Newtonsoft 并不重要。使用 linq 生成指针,调用 .ToString()
,然后您可以使用 Newtonsoft 的结果。
// get a pointer
var pointer = JsonPointer.Create<Foo>(x => x.Bar);
var asString = pointer.ToString();
// use the pointer
暂时不支持自定义大小写;它使用模型的外壳。但这是一个很好的功能创意,我可以相当简单地添加它。它将使用 System.Text.Json 机制而不是 Newtonsoft。
有关详细信息,请参阅 the docs。
对于json路径:
看看Manatee.Json。它绝对可以做到。
它是我 json-everything suite 的前身。
现在已弃用它以支持我的新库,但我尚未将此特定功能添加到新库中。
这里最棘手的部分是获取 属性 的名称,因为它将被序列化。您可以通过以下方式进行操作:
static string GetNameUnderContract(IContractResolver resolver, MemberInfo member)
{
var contract = (JsonObjectContract)resolver.ResolveContract(member.DeclaringType);
var property = contract.Properties.Single(x => x.UnderlyingName == member.Name);
return property.PropertyName;
}
一旦你有了它,你就可以处理表达式的每个级别并将结果弹出到字符串堆栈中。除了简单的成员访问之外,以下快速实现还支持索引。
public string GetJsonPointer<T>(IContractResolver resolver, Expression<Func<T,object>> expression)
{
Stack<string> pathParts = new();
var currentExpression = expression.Body;
while (currentExpression is not ParameterExpression)
{
if (currentExpression is MemberExpression memberExpression)
{
// Member access: fetch serialized name and pop
pathParts.Push(GetNameUnderContract(memberExpression.Member));
currentExpression = memberExpression.Expression;
}
else if (
currentExpression is BinaryExpression binaryExpression and { NodeType: ExpressionType.ArrayIndex }
&& binaryExpression.Right is ConstantExpression arrayIndexConstantExpression
)
{
// Array index
pathParts.Push(arrayIndexConstantExpression.Value.ToString());
currentExpression = binaryExpression.Left;
}
else if (
currentExpression is MethodCallExpression callExpression and { Arguments: { Count: 1 }, Method: { Name: "get_Item" } }
&& callExpression.Arguments[0] is ConstantExpression listIndexConstantExpression and { Type: { Name: nameof(System.Int32) } }
&& callExpression.Method.DeclaringType.GetInterfaces().Any(i=>i. IsGenericType && i.GetGenericTypeDefinition()==typeof(IReadOnlyList<>))
)
{
// IReadOnlyList index of other type
pathParts.Push(listIndexConstantExpression.Value);
currentExpression = callExpression.Object;
}
else
{
throw new InvalidOperationException($"{currentExpression.GetType().Name} (at {currentExpression}) not supported");
}
}
return string.Join("/", pathParts);
}
调用示例:
public record Foo([property: JsonProperty("Barrrs")] Bar[] Bars);
public record Bar(string Baz);
var resolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() };
GetJsonPointer<Foo>(resolver, x => x.Bars[0].Baz).Dump();
//dumps "/Barrrs[0]/baz"