可以使用 JSONPath 进行不区分大小写的搜索吗?

问题描述 投票:0回答:6

使用 JSON.NET 的

SelectToken
方法 使用 JSONPath 选择令牌,我发现没有办法指定搜索应该不区分大小写。

例如

json.SelectToken("$.maxAppVersion")

应该返回一个匹配的标记,无论它是写成

maxappversion
MAXAPPVERSION
还是任何其他大小写。

我的问题:

是否有官方方法或至少有解决方法以不区分大小写的方式使用 JSONPath?

(我发现的最接近的是这个类似的问题,用于 JSON 的 Java 实现)

.net json json.net jsonpath
6个回答
11
投票

自 8.0.2 版本起,Json.NET 尚未实现此功能。

JSONPath 属性名称匹配通过两个类完成:

FieldFilter
用于简单名称匹配,
ScanFilter
用于递归搜索。
FieldFilter
有以下代码,其中
o
JObject

JToken v = o[Name];
if (v != null)
{
    yield return v;
}

在内部

JObject
使用
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 标准中没有提到不区分大小写的匹配,所以我认为你想要的根本无法开箱即用。

作为解决方法,您可以为此添加自己的扩展方法:

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 问题 提供一种进行区分大小写的属性反序列化的方法,该方法请求区分大小写的契约解析器以与 LINQ-to-JSON 的区分大小写保持一致。)


4
投票

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

整个扩展的代码可在此处获取


2
投票

当我想要获得令牌并且不必担心大小写时,我会这样做:

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();
都可以工作,并且我都会得到。


1
投票

对我有用的是将所有内容转换为大写,然后在大写字符串中搜索,但值是从原始字符串返回的:

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;
}

1
投票

一个快速肮脏的黑客解决方案是将对象键转换为大写,然后执行 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*:"


1
投票

我从这个答案中获得灵感,并首先将所有键设为小写。如果您使用 JObject.FromObject 创建 JObject,这很有效。

首先创建一个NamingStrategy

public class LowercaseNamingStrategy : NamingStrategy
{
    protected override string ResolvePropertyName(string name)
    {
        return name.ToLowerInvariant();
    }
}

然后用它来创建你的 JObject

var jObject = JObject.FromObject(
    dotnetObject,
    new JsonSerializer
    {
        ContractResolver = new DefaultContractResolver
        {
            NamingStrategy = new LowercaseNamingStrategy()
        }
    }
);
var value = jObject.SelectToken(path.ToLowerInvariant())
© www.soinside.com 2019 - 2024. All rights reserved.