Possible to search case-insensitive with JSONPath?

前端 未结 3 1240
清歌不尽
清歌不尽 2021-02-15 12:35

Using the SelectToken method of JSON.NET to select a token with JSONPath, I found no way to specifiy that the search should be case-insensitive.

E.g.

jso         


        
相关标签:
3条回答
  • 2021-02-15 13:25

    This is not implemented in Json.NET as of version 8.0.2.

    JSONPath property name matching is done with two classes: FieldFilter for simple name matches, and ScanFilter for recursive searches. FieldFilter has the following code, where o is a JObject:

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

    Internally JObject uses a JPropertyKeyedCollection to hold its properties, which in turn uses the following comparer for property name lookups:

    private static readonly IEqualityComparer<string> Comparer = StringComparer.Ordinal;
    

    It is thus case-sensitive. Similarly, ScanFilter has:

    JProperty e = value as JProperty;
    if (e != null)
    {
        if (e.Name == Name)
        {
            yield return e.Value;
        }
    }
    

    Which is also case sensitive.

    There's no mention of case-insensitive matching in the JSONPath standard so I think what you want simply isn't available out of the box.

    As a workaround, you could add your own extension methods for this:

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

    And then chain them together with standard SelectTokens calls, for instance:

    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
    

    (Relatedly, see the Json.NET issue Provide a way to do case-sensitive property deserialization which requests a case-sensitive contract resolver for consistency with the case-sensitivity of LINQ-to-JSON.)

    0 讨论(0)
  • 2021-02-15 13:27

    When I want to get a token and not have to worry about case I do this:

    var data = JObject.Parse(message.Data);
    var dataDictionary = new Dictionary<string, JToken>(data.ToObject<IDictionary<string, JToken>>(),
                                                            StringComparer.CurrentCultureIgnoreCase);
    

    If there are any nested structures I need to worry about then I have to do it again for those but that StringComparer means either dataDictionary["CampaignId"].ToString(); or dataDictionary["campaignId"].ToString(); will work and I get both.

    0 讨论(0)
  • 2021-02-15 13:30

    It's really surprising that Newtonsoft gets away without supporting this. I had to write a custom JToken extension to support this. I did not need the entire JSONPath, needed just a few basic path queries. Below is the code I used

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

    With the above code I can parse JSON as follows

    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
    

    Code to entire extension available here

    0 讨论(0)
提交回复
热议问题