I can make SelectTokens() return the results you want by using the existence operator rather than the inequality operator:
string path = "$.[?(@.value)]";
Now all 4 objects are returned. Sample fiddle.
As to whether this is a bug, the JSONPath article is ambiguous. It says:
[] ?() applies a filter (script) expression.
n/a () script expression, using the underlying script engine.
But what does using the underlying script engine actually mean? If I test your JSON and query expression at https://jsonpath.curiousconcept.com/, then an error is generated for both Flow Communications JSONPath 0.3.1 and Stefan Goessner JSONPath 0.8.3. If I use my existence query, then the former works correctly but the latter still throws an exception.
Should Json.NET behave as it does currently? The present implementation implicitly provides the ability to filter for properties that have primitive values. The proposed behavior would lose this capability. I.e., any object or array value would always match any inequality operator such as "$.[?(@.value!='aString')]", which might not be expected.
Update
Your clarified requirement is to run a query "$.[?(@.value!=null)]" to return all objects with a property named "value" that exists and has non-null value, regardless of whether that value is a primitive or a nested container.
One the one hand, this seems like a reasonable query that ought to be possible. On the other, the current capability of being able to run inequality queries on primitive values without also getting all container values seems useful and should also be possible. And either interpretation of that particular query string seems plausible; the JSONPath language definition is just too vague and leaves too much up to the interpretation of implementers.
Your options to get what you need include:
You could certainly report an issue about this. Newtonsoft could decide that the current behavior is wrong, and change it.
You could fork your own version of Json.NET and modify BooleanQueryExpression.IsMatch(JToken t) as follows:
case QueryOperator.NotEquals:
if (v != null && !EqualsWithStringCoercion(v, Value))
{
return true;
}
else if (v == null && r != null)
{
return true;
}
break;
You could use the following workaround:
var path = "$.[?(@.value)]";
var tokens = jt.SelectTokens(path, false)
.Where(t => !t["value"].IsNull());
using the extension method:
public static class JsonExtensions
{
public static bool IsNull(this JToken token)
{
return token == null || token.Type == JTokenType.Null;
}
}
Finally, you ask What I don't understand is why it works fine in javascript, but not in selectTokens of the Json.Net.. The reason that Json.NET is behaving differently than, say, http://jsonpath.com/, is that:
The standard is completely ambiguous as to what a script expression is, exactly, and
They are implemented using different technologies and code bases. Json.NET interprets (@.value!=null) to mean
a value of type JValue named "value" that is not equal to the null json primitive.
whereas the other parser interprets the query as
a value named "value" that is not equal to the null json primitive.
The fact that c# is strongly typed as compared to, say, javascript, may explain the difference.