可以使用 JSONPath 搜索不区分大小写的内容吗?
Possible to search case-insensitive with JSONPath?
使用 SelectToken
method of JSON.NET to select a token with JSONPath,我发现无法指定搜索不区分大小写。
例如
json.SelectToken("$.maxAppVersion")
应该return一个匹配的token,不管它是maxappversion
、MAXAPPVERSION
还是任何其他大小写。
我的问题:
是否有官方方法或至少有变通方法以不区分大小写的方式使用 JSONPath?
(我找到的最接近的是 this similar question,用于 JSON 的 Java 实现)
自 8.0.2 版起,Json.NET 中未实现此功能。
JSON路径 属性 名称匹配是通过两个 类: FieldFilter
for simple name matches, and ScanFilter
进行递归搜索。 FieldFilter
具有以下代码,其中 o
是 JObject
:
JToken v = o[Name];
if (v != null)
{
yield return v;
}
在内部 JObject
uses a JPropertyKeyedCollection
保存其属性,它又使用以下比较器进行 属性 名称查找:
private static readonly IEqualityComparer<string> Comparer = StringComparer.Ordinal;
因此区分大小写。同样,ScanFilter
有:
JProperty e = value as JProperty;
if (e != null)
{
if (e.Name == Name)
{
yield return e.Value;
}
}
这也是区分大小写的。
JSONPath standard 中没有提及不区分大小写的匹配,所以我认为您想要的东西根本无法立即使用。
作为解决方法,您可以为此添加自己的扩展方法:
public static class JsonExtensions
{
public static IEnumerable<JToken> CaseSelectPropertyValues(this JToken token, string name)
{
var obj = token as JObject;
if (obj == null)
yield break;
foreach (var property in obj.Properties())
{
if (name == null)
yield return property.Value;
else if (string.Equals(property.Name, name, StringComparison.OrdinalIgnoreCase))
yield return property.Value;
}
}
public static IEnumerable<JToken> CaseSelectPropertyValues(this IEnumerable<JToken> tokens, string name)
{
if (tokens == null)
throw new ArgumentNullException();
return tokens.SelectMany(t => t.CaseSelectPropertyValues(name));
}
}
然后将它们与标准 SelectTokens
调用链接在一起,例如:
var root = new { Array = new object[] { new { maxAppVersion = "1" }, new { MaxAppVersion = "2" } } };
var json = JToken.FromObject(root);
var tokens = json.SelectTokens("Array[*]").CaseSelectPropertyValues("maxappversion").ToList();
if (tokens.Count != 2)
throw new InvalidOperationException(); // No exception thrown
(相关地,请参阅 Json.NET 问题 Provide a way to do case-sensitive property deserialization,该问题要求区分大小写的合同解析器与 LINQ-to-JSON 的区分大小写保持一致。)
当我想获得令牌而不用担心大小写时,我会这样做:
var data = JObject.Parse(message.Data);
var dataDictionary = new Dictionary<string, JToken>(data.ToObject<IDictionary<string, JToken>>(),
StringComparer.CurrentCultureIgnoreCase);
如果有任何我需要担心的嵌套结构,那么我必须为那些再做一次,但是 StringComparer 意味着 dataDictionary["CampaignId"].ToString();
或 dataDictionary["campaignId"].ToString();
都可以工作,我得到了两个。
Newtonsoft 竟然不支持就走了,真是令人惊讶。我必须编写自定义 JToken 扩展来支持这一点。我不需要整个 JSONPath,只需要一些基本的路径查询。下面是我使用的代码
public static JToken GetPropertyFromPath(this JToken token, string path)
{
if (token == null)
{
return null;
}
string[] pathParts = path.Split(".");
JToken current = token;
foreach (string part in pathParts)
{
current = current.GetProperty(part);
if (current == null)
{
return null;
}
}
return current;
}
public static JToken GetProperty(this JToken token, string name)
{
if (token == null)
{
return null;
}
var obj = token as JObject;
JToken match;
if (obj.TryGetValue(name, StringComparison.OrdinalIgnoreCase, out match))
{
return match;
}
return null;
}
通过上面的代码我可以解析JSON如下
var obj = JObject.Parse(someJson);
JToken tok1 = obj.GetPropertyFromPath("l1.l2.l3.name"); // No $, or other json path cliché
JToken tok2 = obj.GetProperty("name");
string name = obj.StringValue("name"); // Code in the link below
整个扩展的代码available here
对我有用的是将所有内容都转换为大写,然后在大写字符串中搜索,但值是从原始字符串返回的:
public static string SelectValue(this string jsonString, string jsonPath)
{
string result = null;
string jsonStringToUpper = jsonString.ToUpper();
string jsonPathToUpper = jsonPath.ToUpper();
var jsonObj = JObject.Parse(jsonStringToUpper);
string valueToUpper = (string)jsonObj.SelectToken(jsonPathToUpper);
if (!string.IsNullOrEmpty(valueToUpper))
{
int upperCaseIndex = jsonStringToUpper.IndexOf(valueToUpper);
result = jsonString.Substring(upperCaseIndex, valueToUpper.Length);
}
return result;
}
一个快速肮脏的 hackish 解决方案是将对象键转换为大写,然后执行 JsonPath 查询,这是一个针对您的情况的简单示例(只是为了给您一个想法)。要包含深度搜索,您必须转换子对象中的所有键(需要递归):
var path = "maxappversion";
var json = "{\"maxAppVersion\": 1}";
var type = typeof(Dictionary<string, int>);
// This will convert the json str to actual Dictionary object
var jsonObj = JsonSerializer.Deserialize(json, type);
// Loop through all of the properties in the dictionary and convert the keys to uppercase
foreach (var kpv in jsonObj) jsonObj[kpv.Key.ToUpperCase()] = kpv.Value;
// Look for the path
try
{
// This will return list of tokens, if not found, exception will be thrown
var tokens = JObject.FromObject(jsonObj).SelectTokens(path.ToUpperCase(), true);
var value = (int)tokens.FirstOrDefault(); // Use linq or just loop
}
catch (JsonException)
{
// PathNotFound
}
请注意,此代码未经测试。
更新:
为了更快地转换密钥,您可以执行正则表达式替换以替换 json 字符串中的所有密钥。模式类似于:"\"([^\"]+)\"\s*:"
使用 SelectToken
method of JSON.NET to select a token with JSONPath,我发现无法指定搜索不区分大小写。
例如
json.SelectToken("$.maxAppVersion")
应该return一个匹配的token,不管它是maxappversion
、MAXAPPVERSION
还是任何其他大小写。
我的问题:
是否有官方方法或至少有变通方法以不区分大小写的方式使用 JSONPath?
(我找到的最接近的是 this similar question,用于 JSON 的 Java 实现)
自 8.0.2 版起,Json.NET 中未实现此功能。
JSON路径 属性 名称匹配是通过两个 类: FieldFilter
for simple name matches, and ScanFilter
进行递归搜索。 FieldFilter
具有以下代码,其中 o
是 JObject
:
JToken v = o[Name];
if (v != null)
{
yield return v;
}
在内部 JObject
uses a JPropertyKeyedCollection
保存其属性,它又使用以下比较器进行 属性 名称查找:
private static readonly IEqualityComparer<string> Comparer = StringComparer.Ordinal;
因此区分大小写。同样,ScanFilter
有:
JProperty e = value as JProperty;
if (e != null)
{
if (e.Name == Name)
{
yield return e.Value;
}
}
这也是区分大小写的。
JSONPath standard 中没有提及不区分大小写的匹配,所以我认为您想要的东西根本无法立即使用。
作为解决方法,您可以为此添加自己的扩展方法:
public static class JsonExtensions
{
public static IEnumerable<JToken> CaseSelectPropertyValues(this JToken token, string name)
{
var obj = token as JObject;
if (obj == null)
yield break;
foreach (var property in obj.Properties())
{
if (name == null)
yield return property.Value;
else if (string.Equals(property.Name, name, StringComparison.OrdinalIgnoreCase))
yield return property.Value;
}
}
public static IEnumerable<JToken> CaseSelectPropertyValues(this IEnumerable<JToken> tokens, string name)
{
if (tokens == null)
throw new ArgumentNullException();
return tokens.SelectMany(t => t.CaseSelectPropertyValues(name));
}
}
然后将它们与标准 SelectTokens
调用链接在一起,例如:
var root = new { Array = new object[] { new { maxAppVersion = "1" }, new { MaxAppVersion = "2" } } };
var json = JToken.FromObject(root);
var tokens = json.SelectTokens("Array[*]").CaseSelectPropertyValues("maxappversion").ToList();
if (tokens.Count != 2)
throw new InvalidOperationException(); // No exception thrown
(相关地,请参阅 Json.NET 问题 Provide a way to do case-sensitive property deserialization,该问题要求区分大小写的合同解析器与 LINQ-to-JSON 的区分大小写保持一致。)
当我想获得令牌而不用担心大小写时,我会这样做:
var data = JObject.Parse(message.Data);
var dataDictionary = new Dictionary<string, JToken>(data.ToObject<IDictionary<string, JToken>>(),
StringComparer.CurrentCultureIgnoreCase);
如果有任何我需要担心的嵌套结构,那么我必须为那些再做一次,但是 StringComparer 意味着 dataDictionary["CampaignId"].ToString();
或 dataDictionary["campaignId"].ToString();
都可以工作,我得到了两个。
Newtonsoft 竟然不支持就走了,真是令人惊讶。我必须编写自定义 JToken 扩展来支持这一点。我不需要整个 JSONPath,只需要一些基本的路径查询。下面是我使用的代码
public static JToken GetPropertyFromPath(this JToken token, string path)
{
if (token == null)
{
return null;
}
string[] pathParts = path.Split(".");
JToken current = token;
foreach (string part in pathParts)
{
current = current.GetProperty(part);
if (current == null)
{
return null;
}
}
return current;
}
public static JToken GetProperty(this JToken token, string name)
{
if (token == null)
{
return null;
}
var obj = token as JObject;
JToken match;
if (obj.TryGetValue(name, StringComparison.OrdinalIgnoreCase, out match))
{
return match;
}
return null;
}
通过上面的代码我可以解析JSON如下
var obj = JObject.Parse(someJson);
JToken tok1 = obj.GetPropertyFromPath("l1.l2.l3.name"); // No $, or other json path cliché
JToken tok2 = obj.GetProperty("name");
string name = obj.StringValue("name"); // Code in the link below
整个扩展的代码available here
对我有用的是将所有内容都转换为大写,然后在大写字符串中搜索,但值是从原始字符串返回的:
public static string SelectValue(this string jsonString, string jsonPath)
{
string result = null;
string jsonStringToUpper = jsonString.ToUpper();
string jsonPathToUpper = jsonPath.ToUpper();
var jsonObj = JObject.Parse(jsonStringToUpper);
string valueToUpper = (string)jsonObj.SelectToken(jsonPathToUpper);
if (!string.IsNullOrEmpty(valueToUpper))
{
int upperCaseIndex = jsonStringToUpper.IndexOf(valueToUpper);
result = jsonString.Substring(upperCaseIndex, valueToUpper.Length);
}
return result;
}
一个快速肮脏的 hackish 解决方案是将对象键转换为大写,然后执行 JsonPath 查询,这是一个针对您的情况的简单示例(只是为了给您一个想法)。要包含深度搜索,您必须转换子对象中的所有键(需要递归):
var path = "maxappversion";
var json = "{\"maxAppVersion\": 1}";
var type = typeof(Dictionary<string, int>);
// This will convert the json str to actual Dictionary object
var jsonObj = JsonSerializer.Deserialize(json, type);
// Loop through all of the properties in the dictionary and convert the keys to uppercase
foreach (var kpv in jsonObj) jsonObj[kpv.Key.ToUpperCase()] = kpv.Value;
// Look for the path
try
{
// This will return list of tokens, if not found, exception will be thrown
var tokens = JObject.FromObject(jsonObj).SelectTokens(path.ToUpperCase(), true);
var value = (int)tokens.FirstOrDefault(); // Use linq or just loop
}
catch (JsonException)
{
// PathNotFound
}
请注意,此代码未经测试。
更新:
为了更快地转换密钥,您可以执行正则表达式替换以替换 json 字符串中的所有密钥。模式类似于:"\"([^\"]+)\"\s*:"