Limitations

edit

The library implements RFC 9535 with opt-in typed function extensions. This page lists what that leaves out, the limits that protect your application from hostile input, and the precision of number comparisons.

Other JSONPath dialects

Before RFC 9535, JSONPath was an informal syntax with many variants. Their extensions are not supported, and Parse rejects most of them:

  • No script expressions: $.book[(@.length-1)] is invalid. Write $.book[-1].
  • No .length property: $.books.length is valid but selects a member named length. Count the matches instead, or use length() in a filter.
  • No other operators: no =~, in, nin, contains or regular expression literals (/.../). Use match() and search().
  • No parent or property name operators (^, ~), and no filters evaluated as JavaScript or C# code: filters use the standard operators and functions only.
  • Only five functions by default. Domain-specific functions require an explicit custom function registry.
var document = JsonNode.Parse("""{"books":[{"title":"A"},{"title":"B"}]}""");

// .length selects a member named "length", not the length of the array
Assert.Empty(JsonPath.Parse("$.books.length").Evaluate(document));

// Count the matches instead
Assert.HasCount(2, JsonPath.Parse("$.books[*]").Evaluate(document));

Filters written as [?(@.price < 10)], with parentheses, are valid RFC 9535 and work unchanged.

Other features

  • No changes through paths. There is no method to set, add or remove values by path. With JsonNode, change the matched nodes yourself; see Change the matched values.
  • No streaming. The query runs on a parsed document (JsonNode, JsonDocument, JsonElement) or on your own model, not on a Utf8JsonReader or a stream.
  • Regular expressions are I-Regexp only. match() and search() do not accept .NET-specific syntax, and ^ and $ are not anchors. See Write the patterns.

Limits

For application-selected cancellation, work, final-match and intermediate-result limits, see Cancel or bound evaluation. These limits throw instead of silently truncating results and do not replace the structural limits below.

  • Queries: filter expressions, nested filter selectors and function arguments nest up to 64 levels deep. Beyond that, Parse throws a FormatException and TryParse returns false, so a hostile or generated query cannot exhaust the stack.
  • Documents: descendant segments (..) and comparisons of arrays and objects visit at most 256 levels of nesting. Beyond that, evaluation throws a JsonPathEvaluationException, in both evaluation modes. Documents parsed by System.Text.Json with its default MaxDepth of 64 never reach this limit; it only affects values built in code, parsed with a higher MaxDepth, or exposed by a custom navigator.
  • Indexes and slices: the numbers of index and slice selectors must be between -(253)+1 and 253-1, the I-JSON range; Parse rejects larger ones.

Number precision

Filters compare numbers, the literals of the query and the values of the document, as double values. Every integer between -(253)+1 and 253-1 and every number that a double represents exactly compare exactly. Outside that range, distinct numbers can compare as equal:

  • Integers beyond ±(2^53)-1 are rounded: $[?@ == 9007199254740993] matches 9007199254740992.
  • Decimals are rounded to the nearest double: $[?@ == 0.1] matches 0.10000000000000001.
  • Numbers too large for a double become infinity: $[?@ == 1e400] matches 1e500.
  • Numbers too small for a double become zero: $[?@ < 0] does not match -1e-400.

When exact values matter, such as identifiers or amounts of money, store them as strings in the document, or compare them in your code after the query.