Possible to search case-insensitive with JSONPath?

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


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.)