Errors and evaluation modes
editA query can fail at two moments: when it is parsed, because it is not valid JSONPath, and when it is evaluated, because the document does not have the expected shape.
Invalid queries
JsonPath.Parse throws a FormatException, and JsonPath.TryParse returns false, for every query that is not well-formed and valid, as RFC 9535 requires. Some of them look plausible:
// Well-formed and valid queries only: these look plausible but are rejected
Assert.False(JsonPath.TryParse("$.store.book[(@.length-1)]", out _)); // script expressions are not JSONPath
Assert.False(JsonPath.TryParse("$[?@.price = 10]", out _)); // the equality operator is ==
Assert.False(JsonPath.TryParse("$[?!@.a == 1]", out _)); // write !(@.a == 1)
Assert.False(JsonPath.TryParse("$[?length(@.*) > 1]", out _)); // length() takes a single value
Assert.False(JsonPath.TryParse("$[?count(@.*)]", out _)); // count() returns a number, not a test
Assert.False(JsonPath.TryParse("$[?upper(@.name) == 'A']", out _)); // unknown function
Assert.False(JsonPath.TryParse("$[?@.name =~ /^A/]", out _)); // use match() or search()The message of the FormatException describes the problem. Use TryParse for queries that users type or that come from configuration files, and Parse for queries written in your code. The rules for comparisons and functions are in Filters and Functions.
Lax and strict evaluation
Evaluate and EvaluateValue take an optional JsonPathEvaluationMode:
- JsonPathEvaluationMode.Lax, the default, follows RFC 9535: a selector that does not apply selects nothing. A missing member, an index outside the array or a name on an array is not an error.
- JsonPathEvaluationMode.Strict throws a JsonPathEvaluationException for these cases instead. Use it when the document must have the shape the query expects, for example to validate a configuration file.
var doc = JsonNode.Parse("""{"a": 1}""");
var path = JsonPath.Parse("$.name");
var laxValue = path.EvaluateValue(doc, JsonPathEvaluationMode.Lax);
Assert.Null(laxValue);
Assert.Throws<JsonPathEvaluationException>(() => path.EvaluateValue(doc, JsonPathEvaluationMode.Strict));What strict mode checks
Strict mode checks the selectors of the child segments of the query (.name, [0], [*], [1:3], [?...]), on every node they apply to. A filter test or a descendant segment never throws, because looking for something that may not be there is what they are for:
var document = JsonNode.Parse("""{"items":[{"id":1},{"id":2,"tag":"new"}]}""");
// Filters and descendant segments test nodes: a missing member is not an error, even in strict mode
var tagged = JsonPath.Parse("$.items[?@.tag == 'new'].id").Evaluate(document, JsonPathEvaluationMode.Strict);
Assert.Single(tagged);
var tags = JsonPath.Parse("$..tag").Evaluate(document, JsonPathEvaluationMode.Strict);
Assert.Single(tags);
// A name, index, wildcard, slice or filter selector that does not apply to the node throws
var exception = Assert.Throws<JsonPathEvaluationException>(
() => JsonPath.Parse("$.items[*].tag").Evaluate(document, JsonPathEvaluationMode.Strict));
Assert.Equal("Path error at $['items'][0]: Object member 'tag' does not exist", exception.Message);In strict mode, a child segment throws when:
- a name selector applies to a value that is not an object, or the object has no such member;
- an index selector applies to a value that is not an array, or the index is outside the array;
- a wildcard or a filter selector applies to a value that is neither an object nor an array;
- a slice selector applies to a value that is not an array.
The message gives the normalized path of the node where the selector failed. The whole evaluation fails: no partial result is returned.
Exceptions
| Exception | Thrown by | When |
|---|---|---|
FormatException | Parse | The query is not valid JSONPath, or nests filters and function calls more than 64 levels deep |
ArgumentNullException | Parse, Evaluate, EvaluateValue | The query or the navigator is null |
| JsonPathEvaluationException | Evaluate, EvaluateValue | A path error in strict mode; in both modes, a descendant segment or a comparison that goes more than 256 levels deep |
JsonPathEvaluationException derives from InvalidOperationException. The depth limits are described in Limitations.